什么是产生字谜的最佳策略.
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中解决了字谜.
但你有什么想法?
这些答案中的大多数都非常低效和/或只能提供单字解决方案(无空格).我的解决方案将处理任意数量的单词并且非常有效.
你想要的是一个特里数据结构.这是一个完整的 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"变成"abfoor".
然后当输入的anagram进来时,也会对它的字母进行排序,然后查找它. 它与散列表查找一样快!
对于多个单词,您可以对已排序的字母进行组合,然后进行排序.不过很多不是生成所有组合更快.
(有关更多优化和详细信息,请参阅注释)
请参阅华盛顿大学CSE部门的这项任务.
基本上,你有一个数据结构只有一个单词中每个字母的计数(一个数组适用于ascii,如果你想要unicode支持,则升级到一个地图).你可以减去其中两个字母集; 如果计数是负数,你就知道一个单词不能是另一个单词的字谜.
预处理:
用字母顺序将每个叶子作为一个已知单词构建一个trie。
在搜索时:
将输入字符串视为多集。通过像深度优先搜索中那样遍历索引来找到第一个子词。在每个分支机构,您都可以问,输入的其余部分中是否包含字母X?如果您具有良好的多集表示形式,则这应该是一个恒定时间的查询(基本上)。
一旦有了第一个子词,就可以保留其余的多集并将其作为新输入来查找该字谜的其余部分(如果存在)。
通过记忆扩展此过程,以便更快地查找常见余数多集。
这非常快-每次遍历都保证给出一个实际的子词,并且每次遍历花费的时间都是子词长度的线性时间(根据编码标准,子词通常很小)。但是,如果您真的想要更快的速度,则可以在预处理过程中包括所有n-gram,其中n-gram是连续n个单词的任何字符串。当然,如果W = #words,那么您将从索引大小O(W)跳到O(W ^ n)。也许n = 2是现实的,具体取决于字典的大小。