当前位置:  开发笔记 > 编程语言 > 正文

生成字谜的算法

如何解决《生成字谜的算法》经验,为你挑选了4个好方法。

什么是产生字谜的最佳策略.

An anagram is a type of word play, the result of rearranging the letters
of a word or phrase to produce a new  word or phrase, using all the original
letters exactly once; 
ex.

十一加二十二加一的字谜

小数点我是一个圆点的字谜

天文学家月球凝视者的字谜

起初它看起来很简单,只是混杂字母并生成所有可能的组合.但是,只生成字典中的单词的有效方法是什么呢?

我遇到了这个页面,在Ruby中解决了字谜.

但你有什么想法?



1> FogleBird..:

这些答案中的大多数都非常低效和/或只能提供单字解决方案(无空格).我的解决方案将处理任意数量的单词并且非常有效.

你想要的是一个特里数据结构.这是一个完整的 Python实现.您只需要保存在名为的文件中的单词列表words.txt 您可以在此处尝试拼字游戏字典单词列表:

http://www.isc.ro/lists/twl06.zip

MIN_WORD_SIZE = 4 # min size of a word in the output

class Node(object):
    def __init__(self, letter='', final=False, depth=0):
        self.letter = letter
        self.final = final
        self.depth = depth
        self.children = {}
    def add(self, letters):
        node = self
        for index, letter in enumerate(letters):
            if letter not in node.children:
                node.children[letter] = Node(letter, index==len(letters)-1, index+1)
            node = node.children[letter]
    def anagram(self, letters):
        tiles = {}
        for letter in letters:
            tiles[letter] = tiles.get(letter, 0) + 1
        min_length = len(letters)
        return self._anagram(tiles, [], self, min_length)
    def _anagram(self, tiles, path, root, min_length):
        if self.final and self.depth >= MIN_WORD_SIZE:
            word = ''.join(path)
            length = len(word.replace(' ', ''))
            if length >= min_length:
                yield word
            path.append(' ')
            for word in root._anagram(tiles, path, root, min_length):
                yield word
            path.pop()
        for letter, node in self.children.iteritems():
            count = tiles.get(letter, 0)
            if count == 0:
                continue
            tiles[letter] = count - 1
            path.append(letter)
            for word in node._anagram(tiles, path, root, min_length):
                yield word
            path.pop()
            tiles[letter] = count

def load_dictionary(path):
    result = Node()
    for line in open(path, 'r'):
        word = line.strip().lower()
        result.add(word)
    return result

def main():
    print 'Loading word list.'
    words = load_dictionary('words.txt')
    while True:
        letters = raw_input('Enter letters: ')
        letters = letters.lower()
        letters = letters.replace(' ', '')
        if not letters:
            break
        count = 0
        for word in words.anagram(letters):
            print word
            count += 1
        print '%d results.' % count

if __name__ == '__main__':
    main()

运行程序时,单词将被加载到内存中的trie中.之后,只需键入要搜索的字母,它就会打印结果.它只会显示使用所有输入字母的结果,不会更短.

它过滤输出中的短字,否则结果数量很大.随意调整MIN_WORD_SIZE设置.请记住,只使用"天文学家"作为输入,如果MIN_WORD_SIZE是1,则给出233,549个结果.也许你可以找到一个只包含更多常用英语单词的较短单词列表.

此外,收缩"我是"(来自您的一个示例)将不会显示在结果中,除非您将"im"添加到字典并设置MIN_WORD_SIZE为2.

获取多个单词的技巧是在搜索中遇到完整单词时跳回到trie中的根节点.然后你继续遍历trie直到所有字母都被使用.


错误报告:如果词汇表有两个条目:"foobar"和"foob"(按此顺序),那么代码片段将找不到"boof"的字谜.只有当你颠倒了词表的顺序时,它才会正确返回"foob".我认为这可以通过将另一个`if`子句放入第一个`for`循环来修复,但是我必须把它留给知道Python的人.

2> Jason Cohen..:

对于字典中的每个单词,按字母顺序对字母进行排序.所以"foobar"变成"abfoor".

然后当输入的anagram进来时,也会对它的字母进行排序,然后查找它. 它与散列表查找一样快!

对于多个单词,您可以对已排序的字母进行组合,然后进行排序.不过很多不是生成所有组合更快.

(有关更多优化和详细信息,请参阅注释)



3> hazzen..:

请参阅华盛顿大学CSE部门的这项任务.

基本上,你有一个数据结构只有一个单词中每个字母的计数(一个数组适用于ascii,如果你想要unicode支持,则升级到一个地图).你可以减去其中两个字母集; 如果计数是负数,你就知道一个单词不能是另一个单词的字谜.



4> Tyler..:

预处理:

用字母顺序将每个叶子作为一个已知单词构建一个trie。

在搜索时:

将输入字符串视为多集。通过像深度优先搜索中那样遍历索引来找到第一个子词。在每个分支机构,您都可以问,输入的其余部分中是否包含字母X?如果您具有良好的多集表示形式,则这应该是一个恒定时间的查询(基本上)。

一旦有了第一个子词,就可以保留其余的多集并将其作为新输入来查找该字谜的其余部分(如果存在)。

通过记忆扩展此过程,以便更快地查找常见余数多集。

这非常快-每次遍历都保证给出一个实际的子词,并且每次遍历花费的时间都是子词长度的线性时间(根据编码标准,子词通常很小)。但是,如果您真的想要更快的速度,则可以在预处理过程中包括所有n-gram,其中n-gram是连续n个单词的任何字符串。当然,如果W = #words,那么您将从索引大小O(W)跳到O(W ^ n)。也许n = 2是现实的,具体取决于字典的大小。

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有