我猜这就像在给定一组 Scrabble 拼字游戏的情况下找到可能的单词,这样一个字符只能重复在原始列表中重复的次数。
诀窍是针对包含源字母的集合有效地测试单词文件中每个单词的每个字符。对于每个字符,如果在测试集中找到,则将其从测试集中删除并继续;否则,该词不匹配,继续下一个词。
Python 有一个很好的函数all 用于根据序列中的元素测试一组条件。 all 具有“短路”的附加功能,即一旦一个项目不符合条件,则不再进行测试。因此,如果您的候选词的第一个字母是“z”,而您的源字母中没有“z”,那么测试候选词中的任何其他字母就没有意义了。
我第一次写这篇文章很简单:
matches = []
for word in wordlist:
testset = set(letters)
if all(c in testset for c in word):
matches.append(word)
不幸的是,这里的错误是如果源字母包含单个“m”,则带有多个“m”的单词会错误地匹配,因为每个“m”会单独匹配源测试集中给定的“m”。所以我需要删除每个匹配的字母。
我利用set.remove(item) 返回None(Python 将其视为布尔值False)这一事实,并扩展了用于调用all 的生成器表达式。对于 word 中的每个 c,如果在测试集中找到它,我想另外将其从测试集中删除,例如(伪代码,无效 Python):
all(c in testset and "remove c from testset" for c in word)
由于 set.remove 返回 None,我可以将上面引用的位替换为“not testset.remove(c)”,现在我有了一个有效的 Python 表达式:
all(c in testset and not testset.remove(c) for c in word)
现在我们只需将其包装在一个循环中,检查列表中的每个单词(确保在检查每个单词之前构建一个新的测试集,因为我们的 all 测试现在已成为破坏性测试):
for word in wordlist:
testset = set(letters)
if all(c in testset and not testset.remove(c) for c in word):
matches.append(word)
最后一步是按长度降序对匹配项进行排序。我们可以传递一个关键函数来排序。内置 len 会很好,但这会按长度升序排序。要将其更改为降序排序,我们使用 lambda 给我们的不是len,而是-1 * len:
matches.sort(key=lambda wd: -len(wd))
现在您可以在matches[0]处打印出最长的单词,或者遍历所有匹配项并将它们打印出来。
(我很惊讶这种蛮力方法运行得如此之好。我使用了 2of12inf.txt 单词列表,包含超过 80,000 个单词,对于 10 个字符的列表,我在大约 0.8 秒内得到匹配列表我的小 1.99GHz 笔记本电脑。)