我之前已经知道过这种变化,但是我无法理解之前的任何实现,因为大多数都涉及使用sets和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()
这个实现的明显问题是会发现一些单词在“字母”中使用多个字母。例如,尽管字母列表中只有一个“a”和“r”副本,但“纸板”这个词显示为有效单词。如何在列表中使用“issubset”方法?
答案 0 :(得分:5)
要知道你是否能用一组字母说出一个字[哎呀,我自己做了 - 我的意思是'收集'!],你希望每个字母至少发生一次,所以我我认为我们必须以某种方式在那里工作。根据定义,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
计数器是一个方便的实用程序类:
>>> 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)))
>>>
答案 1 :(得分:1)
如果你寻找字谜,换句话说你想要重新排列,但是要使用它们(而不是只使用一个子集),那么还有另一个解决方案。
您首先预处理字典中的所有单词。给出一个单词,您可以按字母顺序生成用相同字母书写的单词:
def alphabetize(word):
"".join(sorted(word))
并将这些新单词放入集newDictionary
然后你的函数可以在字母上调用alphabetize并检查结果是否在字典中。
def solve(newDictionary, letters):
query = alphabetize(letters)
return query in newDictionary
字母顺序功能是字谜的一个特征:两个单词是彼此的字谜,当且仅当它们应用字母顺序时产生相同的结果。
答案 2 :(得分:0)
导入collections
,我们定义了一个可哈希的多重集:
def Letters(x):
return frozenset(Counter(x).items())
我们现在将词汇预处理成一个字母词典 - &gt; {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'}