Python-给定输入字母的可能的英语单字字谜
Posted
技术标签:
【中文标题】Python-给定输入字母的可能的英语单字字谜【英文标题】:Python- Possible English one-word anagrams given input letters 【发布时间】:2011-09-13 16:09:30 【问题描述】:我知道以前有人问过这个问题的变体,但我无法理解以前的任何实现,因为它们中的大多数都涉及使用集合和 issubset 方法。
这是我想要做的:我在字典中有一组单词和一个可能的字母列表。我想通过重新排列列表中的字母来查找是否可以形成集合的成员。这是我当前的实现:
def solve(dictionary, letters):
for word in dictionary: #for each word in the dictionary
if len(word) > len(letters): # first optimization, doesn't check words that are larger than letter set
continue
else:
scrambledword = "".join([b for b in sorted(list(word))]) #sorts the letters in each word
if set(scrambledword).issubset(letters):
print word
def main():
dictionary = set([x.strip() for x in open("C:\\Python27\\dictionary.txt")])
letters = sorted(['v','r','o','o','m','a','b','c','d'])
solve(dictionary, letters)
main()
这个实现的明显问题是会发现一些单词在“字母”中使用了多个字母。例如,单词“cardboard”显示为有效单词,尽管字母列表中只有一个“a”和“r”副本。如何在列表中使用“issubset”方法?
【问题讨论】:
'cardboard' 不会被if len(word) > len(letters):
过滤掉吗?
没有。因为单词的长度等于字母列表的长度。
【参考方案1】:
要知道你是否可以用一组字母组成一个词 [哎呀,我自己做了——我的意思是“收集”!],你希望每个字母至少出现正确的次数,所以我认为我们将不得不以某种方式在那里进行计数。根据定义,Python 集不关心源列表中的元素数量。也许像
from collections import Counter
letters = ['v','r','o','o','m','a','b','c','d']
words = 'cardboard boom booom'.split()
letterscount = Counter(letters)
for word in words:
wordcount = Counter(word)
print word, all(letterscount[c] >= wordcount[c] for c in wordcount)
给予
cardboard False
boom True
booom False
Counter 是一个方便的实用程序类:
>>> c = Counter(letters)
>>> c
Counter('o': 2, 'a': 1, 'c': 1, 'b': 1, 'd': 1, 'm': 1, 'r': 1, 'v': 1)
>>> c['o']
2
>>> c['z']
0
[帝斯曼:回归!我删除了一个不起作用的社区编辑,因为 Counter 实例不可散列。]
如果搜索速度是一个问题,那么您可以权衡内存和预计算时间:
from collections import defaultdict, Counter
from itertools import combinations
# precomputations
allwords = open('/usr/share/dict/words').read().split()
allwords = list(w for w in allwords if len(w) >= 3) # hack, /words contains lots of silliness
allwords_by_count = defaultdict(list)
for i, word in enumerate(allwords):
allwords_by_count[frozenset(word)].append((word, Counter(word)))
if i % 1000 == 0:
print i, word
def wordsfrom(letters, words_by_count):
lettercount = Counter(letters)
for subsetsize in range(1, len(lettercount)+1):
for subset in combinations(lettercount, subsetsize):
for possword, posswordcount in words_by_count[frozenset(subset)]:
if all(posswordcount[c] <= lettercount[c] for c in posswordcount):
yield possword
>>> wordsfrom('thistles', allwords_by_count)
<generator object wordsfrom at 0x1032956e0>
>>> list(wordsfrom('thistles', allwords_by_count))
['ess', 'sis', 'tit', 'tst', 'hei', 'hie', 'lei', 'lie', 'sie', 'sise', 'tie', 'tite', 'she', 'het', 'teth', 'the', 'els', 'less', 'elt', 'let', 'telt', 'set', 'sett', 'stet', 'test', 'his', 'hiss', 'shi', 'sish', 'hit', 'lis', 'liss', 'sil', 'lit', 'til', 'tilt', 'ist', 'its', 'sist', 'sit', 'shies', 'tithe', 'isle', 'sile', 'sisel', 'lite', 'teil', 'teli', 'tile', 'title', 'seit', 'sesti', 'site', 'stite', 'testis', 'hest', 'seth', 'lest', 'selt', 'lish', 'slish', 'hilt', 'lith', 'tilth', 'hist', 'sith', 'stith', 'this', 'list', 'silt', 'slit', 'stilt', 'liesh', 'shiel', 'lithe', 'shiest', 'sithe', 'theist', 'thesis', 'islet', 'istle', 'sistle', 'slite', 'stile', 'stilet', 'hitless', 'tehsil', 'thistle']
[呵呵。我刚刚注意到'thistles'本身不在列表中,但那是因为它不在单词文件中..]
是的,单词文件中确实存在明显的“非单词”:
>>> assert all(w in allwords for w in (wordsfrom('thistles', allwords_by_count)))
>>>
【讨论】:
它运行良好,但在非常大的列表上速度非常慢。让我确保我知道它是如何工作的:所以循环将遍历单词中的所有字母,确保“字母列表”中每个字母的频率大于或等于实际单词中字母的频率?非常聪明的实现。我将如何只打印最大字母的单词?我想使用“max()”函数,但我不知道它是如何工作的。 我可能不得不收回关于它“非常慢”的评论,因为我现在已经测试了我在其他线程上看到的以前的实现,它们都成倍地慢。荣誉;但我仍在寻找对此的任何优化。 @NinjaGecko: Error-line 24, in countsToWords.setdefault(key,).add(word) TypeError: unhashable type: 'Counter'【参考方案2】:如果您查找字谜,换句话说,您想重新排列,但使用所有字谜(而不是只使用一个子集),那么还有另一种解决方案。
您首先预处理字典中的所有单词。给定一个单词,你生成的单词是用相同的字母但按字母顺序写的:
def alphabetize(word):
"".join(sorted(word))
并将这些新词放在一组newDictionary
然后你的函数可以在字母上调用Alphabetize并检查结果是否在字典中。
def solve(newDictionary, letters):
query = alphabetize(letters)
return query in newDictionary
Alphabetize 函数是 anagrams 的一个特征:当且仅当它们在应用 alphabetize 时产生相同的结果时,两个单词是彼此的字谜。
【讨论】:
【参考方案3】:导入collections
,我们定义一个hashable multiset:
def Letters(x):
return frozenset(Counter(x).items())
我们现在将词汇表预处理为字母字典->anagram1,anagram2,...:
vocabulary = ['apple', 'banana', 'rats', 'star', 'tars']
countsToWords = defaultdict(lambda: set())
for word in vocabulary:
countsToWords[Letters(word)].add(word)
您的“解决”功能现在需要 O(1) 时间:
def solve(query):
return countsToWords[Letters(query)]
例子:
print( solve('star') )
# 'tars', 'star', 'rats'
【讨论】:
这个实现非常快,但它不会给你长度低于字符串长度的单词。例如 solve('rabbit') 不会产生任何结果以上是关于Python-给定输入字母的可能的英语单字字谜的主要内容,如果未能解决你的问题,请参考以下文章