在 Haskell 中生成下一个字典字符串

Posted

技术标签:

【中文标题】在 Haskell 中生成下一个字典字符串【英文标题】:Generate next lexicographical string in Haskell 【发布时间】:2016-03-17 14:14:01 【问题描述】:

如果给我一个像skhfbvqa 这样的字符串,我将如何生成 next 字符串?对于这个例子,它是skhfbvqb,下一个字符串是skhfbvqc,以此类推。给定的字符串(和答案)总是 N 个字符长(在这种情况下,N=8)。

我尝试了什么:

我试图生成可能组合的整个(无限)列表,并获取给定字符串的所需(下一个)字符串,但不出所料,它太慢了,我什至没有得到 的答案N=6.

我使用了列表理解:

allStrings = [ c : s | s <- "" : allStrings, c <- ['a'..'z'] ]

main = do
    input <- readFile "k.in"
    putStrLn . head . tail . dropWhile (not . (==) input) . map reverse $ allStrings

(请原谅我非常糟糕的 Haskell-ing :) 仍然是菜鸟)

所以我的问题是,我该怎么做?如果有多种方法,非常感谢它们之间的比较。谢谢!

【问题讨论】:

扫描allStrings 看起来非常低效——它类似于通过\x -&gt; head . tail $ dropWhile (/= x) [0..] 计算后继。它有效,但需要指数时间/空间。相反,我会将字符串表示为向后列表,并对每个字符/数字执行标准的“带进位加法”。正如预期的那样,这将具有线性的最坏情况复杂性。 (此外,它也是摊销常数时间,因为几何和收敛,因此是最优的) @chi 是的,allStrings 效率极低。这只是我想到的第一件事。你的建议听起来很有趣。您能否提供一个答案,但提供更多指示/步骤? 【参考方案1】:

这是一个带有基本转换的版本(这样你可以随意加减):

encode x base = encode' x [] where
  encode' x' z | x' == 0 = z
               | otherwise = encode' (div x' base) ((mod x' base):z)

decode num base =
  fst $ foldr (\a (b,i) -> (b + a * base^i,i + 1)) (0,0) num

输出:

*Main> map (\x -> toEnum (x + 97)::Char) 
       $ encode (decode (map (\x -> fromEnum x - 97) "skhfbvqa") 26 + 1) 26

"skhfbvqb"

【讨论】:

感谢您的回答!所以这就像将字符串视为以 26 为基数的数字,对吧?使用字符 [a-z] 而不是数字。 @Roshnal 是的,完全正确 - 就像将字符串视为 base-26 数字,使用字符 [a-z] 而不是数字。 @Roshnal 它可能需要对具有 as(视为零)作为前缀的字符串进行一些填充 啊,是的,没错。有趣的方法,也会尝试一下:)【参考方案2】:

我会去创建一个辅助函数f :: Integer -&gt; String 和一个g :: String -&gt; Integer,其中f 1 = "a",...f 27 = "aa"f 28 = "ab" 等等,反之则为g

然后incrementString = f . succ . g

注意:为了学习,我省略了f的实现

更新

对于不同的方法,您可以使用进位函数 inc' :: Char -&gt; (Char, Bool) 定义增量,然后

incString :: String -> String
incString = reverse . incString'
   where incString' [] = []
         incString' (x:xs) = case inc' x of (x',True) -> x': incString' xs
                                            (x',False) -> x':xs

注意:这个函数不是尾递归的!

【讨论】:

感谢您的回复。没想到是这样的;会试试的!但是我想知道这是否可以以更“Haskell-y”的方式完成(例如使用内置函数、monad 等)【参考方案3】:

我发现这行得通。它只是使用模式匹配来查看字符串是否以 z 开头并相应地添加额外的 a

incrementString' :: String -> String
incrementString' []          = ['a']
incrementString' ('z':xs)    = 'a' : incrementString' xs
incrementString' (x:xs)      = succ x : xs

incrementString :: String -> String
incrementString = reverse . incrementString' . reverse

【讨论】:

这对我来说非常好。检查incrementString "zzzzzzzz" 是否是您真正想要的——我提到这一点是因为您写道答案的长度必须为 8。 @chi 你说得对,那里有问题。它给出了 9 a's 的输出......但我的意思是问题提供者(这是一个更大的练习问题的一部分)提到答案将是 N 位数。再次阅读我的问题,看来我可能有点不清楚。对不起:/

以上是关于在 Haskell 中生成下一个字典字符串的主要内容,如果未能解决你的问题,请参考以下文章

在 haskell 中,如何从十进制数中生成二进制数字?

如何使用 Perl 从一组字母中生成单词列表?

Django路由系统(一)

如何在 Haskell 中解析 IO 字符串?

Haskell“字符串移动”函数

在Haskell中,如何创建具有多种类型的列表?