如何生成长度为n
的所有可能的回文?
应该使用唯一的字符['a'..'z']
palindrome n :: Integer -> [String]
答案 0 :(得分:1)
回文是一个字符串,其中字符的后半部分与字符的前半部分相反。因此,一个简单的算法是生成长度为n / 2
的所有字符串,然后将每个字符串的反向追加到末尾。对于奇数长度的回文,我们可以删除字符串后半部分的第一个字符,并确保在找到n / 2
时向上舍入。
现在棘手的部分是生成所有可能的长度为n / 2
的字符串。我们需要为['a'..'z']
中的字符串中的每个字符选择一个字符,并在Haskell中lists can represent non-determinism。因此,我们需要做的就是使用replicateM
,它将创建每个字符串,其中每个字符都是从字母表中非确定性地选择的。
附注,任何长度n
可能的回文数以指数速率增加。使用Integer
作为输入是过度的,因为Int
的最大值已经超过9 quintillion。
这是实现完整算法的一种方法:
palindrome :: Int -> [String]
palindrome n
| n < 0 = []
| even n = map (\front -> front ++ reverse front) fronts
| odd n = map (\front -> front ++ tail (reverse front)) fronts
where fronts = replicateM (div (n + 1) 2) ['a'..'z']
答案 1 :(得分:0)
为简单起见,首先假设n
是偶数,稍后我们可以概括该函数。我们可以使用递归。我们定义一个辅助函数pal' :: Integer -> [String] -> [String]
。这里的第二个参数是累积的反向字符串。所以一旦我们点击0
,我们只需要发出一个包含累积反转字符串的列表,如:
pal' 0 r = [r]
然而,鉴于我们仍然在回文的生成部分,左侧,我们可以使用列表理解,如:
pal' k r = [ c : p | c <- ['a'..'z'], p <- pal' (k-1) (c:r) ]
所以我们迭代[a..z]
,对于每个这样的字符,我们在pal'
执行递归调用,我们需要生成k-1
字符,(c:r)
作为要发射的反向弦。此外,我们得到这些回文c : p
。因此,我们将选择角色放在回文前面。
现在,对于生成甚至回文的even_palindrome
函数,我们可以写一下:
evenpalindrome :: Integer -> [String]
evenpalindrome n = pal' (div n 2) []
where pal' 0 r = [r]
pal' k r = [ c : p | c <- ['a'..'z'], p <- pal' (k-1) (c:r) ]
出于测试目的,我将代码设置为从`c&lt; - ''''..'d']范围中选择,但您可以将其设置为您想要的任何范围。
如果我们生成长度为0,2和4的回文,我们得到:
*Main> evenpalindrome 0
[""]
*Main> evenpalindrome 2
["aa","bb","cc","dd"]
*Main> evenpalindrome 4
["aaaa","abba","acca","adda","baab","bbbb","bccb","bddb","caac","cbbc","cccc","cddc","daad","dbbd","dccd","dddd"]
所以这似乎有效。但是,如果我们写evenpalindrome 1
,那么它将起到整数除法的作用,从而生成长度为0的回文。
现在的问题是我们必须改变什么才能让它适用于奇数长度。这里有两件事需要改变:
div n 2
,而是使用div (n+1) 2
;和所以这意味着我们应该首先检查n
模2(让它为d
),然后重写:
pal' 0 r = [drop (fromInteger d) r]
此外如前所述,我们应该使用pal'
调用初始pal' (div (n+1) 2) []
,所以现在通用版本是:
palindrome :: Integer -> [String]
palindrome n = pal' (div (n+1) 2) []
where pal' 0 r = [drop (fromInteger d) r]
pal' k r = [ c : p | c <- ['a'..'z'], p <- pal' (k-1) (c:r) ]
d = mod n 2
产生:
*Main> palindrome 1
["a","b","c","d"]
*Main> palindrome 2
["aa","bb","cc","dd"]
*Main> palindrome 3
["aaa","aba","aca","ada","bab","bbb","bcb","bdb","cac","cbc","ccc","cdc","dad","dbd","dcd","ddd"]
*Main> palindrome 4
["aaaa","abba","acca","adda","baab","bbbb","bccb","bddb","caac","cbbc","cccc","cddc","daad","dbbd","dccd","dddd"]
*Main> palindrome 5
["aaaaa","aabaa","aacaa","aadaa","ababa","abbba","abcba","abdba","acaca","acbca","accca","acdca","adada","adbda","adcda","addda","baaab","babab","bacab","badab","bbabb","bbbbb","bbcbb","bbdbb","bcacb","bcbcb","bcccb","bcdcb","bdadb","bdbdb","bdcdb","bdddb","caaac","cabac","cacac","cadac","cbabc","cbbbc","cbcbc","cbdbc","ccacc","ccbcc","ccccc","ccdcc","cdadc","cdbdc","cdcdc","cdddc","daaad","dabad","dacad","dadad","dbabd","dbbbd","dbcbd","dbdbd","dcacd","dcbcd","dcccd","dcdcd","ddadd","ddbdd","ddcdd","ddddd"]
使用递归方式构造反向部分的一个好处是,所有回文的后半部分都能更有效地存储。鉴于我们为`['a'..'b']范围生成长度为5的回文,最终列表(完成评估后)将如下所示:
+---+ | o--- 'a' -- 'a' -> 'a' -\ +---+ > 'a' -\ | o--> 'a' -> 'a' -> 'b' -/ \ +---+ > 'a' | o--> 'a' -> 'b' -> 'a' -\ / +---+ > 'b' -/ | o--> 'a' -> 'b' -> 'b' -/ +---+ | o--> 'b' -> 'a' -> 'a' -\ +---+ > 'a' -\ | o--> 'b' -> 'a' -> 'b' -/ \ +---+ > 'b' | o--> 'b' -> 'b' -> 'a' -\ / +---+ > 'b' -/ | o--> 'b' -> 'b' -> 'b' -/ +---+