最后,我想找出英文词典中哪个词包含至少三个字母的最多子词。我写了这个算法,但它太慢而无法使用。想知道我可以优化它的方法
def subWords(word):
return set((word[0:i] for i in range(2, len(word)+1))) #returns all subWords of length 2 or greater
def checkDict(wordList, dictList):
return set((word for word in wordList if word in dictList))
def main():
dictList = [i.strip() for i in open('wordlist.txt').readlines()]
allwords = list()
maximum = (0, list())
for dictWords in dictList:
for i in range (len(dictWords)):
for a in checkDict(subWords(dictWords[i: len(dictWords) + 1]), dictList):
allwords.append(a)
if len(allwords) > maximum[0]:
maximum = (len(allwords), allwords)
print maximum
allwords = list()
print maximum
main()
答案 0 :(得分:7)
算法的主要缺点是,对于每个子字,您需要将其与字典中的每个其他字进行比较。你不需要这样做,真的 - 如果你的单词以'a'开头,你真的不需要看它是否匹配以'b'开头的单词。如果下一个字母是'c',那么你真的不想将它与以'd'开头的单词进行比较。那么问题就变成:“我如何有效地实现这个想法?”
为此,我们可以创建一个树来表示字典中的所有单词。我们通过获取字典中的每个单词并使用它扩展树,并在最后一个节点中进行着色来构造此树。
当我们想要测试子树是否在这个树中时,我们只是逐字逐句地查看该单词并使用这些字母来确定树中的下一个位置(从顶部开始)。如果我们发现我们无处可去,或者在经过整个子词后我们落在非阴影树节点上,那么它就不是一个字。否则,如果我们落在阴影节点上,它就是一个单词。这样做的结果是我们可以一次搜索整个字典 ,而不是一次搜索一个字。当然,这样做的成本在一开始就是一些设置,但如果你在字典里有很多单词,这不是一个很好的代价。
嗯,这太棒了!让我们尝试实现它:
class Node:
def __init__( self, parent, valid_subword ):
self.parent = parent
self.valid_subword = valid_subword
self.children = {}
#Extend the tree with a new node
def extend( self, transition, makes_valid_word ):
next_node = None
if transition in self.children:
if makes_valid_word:
self.children[transition].makes_valid_word = True
else:
self.children[transition] = Node( self, makes_valid_word )
return self.children[transition]
def generateTree( allwords ):
tree = Node( None, False )
for word in allwords:
makes_valid_word = False
current_node = tree
for i in range(len(word)):
current_node = current_node.extend( word[i], True if i == len(word) - 1 else False )
return tree
def checkDict( word, tree ):
current_node = tree
for letter in word:
try:
current_node = current_node.children[letter]
except KeyError:
return False
return current_node.valid_subword
然后,后来:
for word in allWords:
for subword in subWords(word):
checkDict(subword)
#Code to keep track of the number of words found, like you already have
此算法允许您在 O(m)时间内检查词典中是否有单词,其中m是词典中最长单词的长度。请注意,对于包含任意数量单词的字典,这仍然大致保持不变。您的原始算法每次检查 O(n),其中n是字典中的字数。
答案 1 :(得分:6)
1)风格和组织:使用单个函数生成单词的所有子词更有意义。
2)样式:使用set
不需要双括号。
3)表现(我希望):从你正在查找的单词中做出set
;那么你可以使用内置的交集检查。
4)性能(几乎可以肯定):不要手动循环来找到最大元素;使用内置的max
。你可以直接比较(长度,元素)元组; Python从头到尾比较每对元素的元组,就像每个元素都是字符串中的字母一样。
5)表演(可能):确保词典中没有单词或双字母单词,因为它们只是妨碍了。
6)表现(遗憾的是):不要将一切分解为一个功能。
7)样式:文件I / O应使用with
块来确保正确清理资源,并且文件迭代器默认迭代行,因此我们可以隐式地获取行列表而不必调用.readlines()
。
我最终(没有经过适当测试,除了'片段'表达式):
def countedSubWords(word, dictionary):
fragments = set(
word[i:j]
for i in range(len(word)) for j in range(i+3, len(word)+1)
)
subWords = fragments.intersection(dictionary)
return (len(subWords), subWords)
def main():
with open('wordlist.txt') as words:
dictionary = set(word.strip() for word in words if len(word.strip()) > 2)
print max(countedSubWords(word, dictionary) for word in dictionary)
答案 2 :(得分:3)
要探索基本的Python,请看一下这个函数(基本上是一个更快,更完美,PEP8 - JBernardo和Karl Knechtel建议的快乐版本:
def check_dict(word, dictionary):
"""Return all subwords of `word` that are in `dictionary`."""
fragments = set(word[i:j]
for i in xrange(len(word) - 2)
for j in xrange(i + 3, len(word) + 1))
return fragments & dictionary
dictionary = frozenset(word for word in word_list if len(word) >= 3)
print max(((word, check_dict(word, dictionary)) for word in dictionary),
key=lambda (word, subwords): len(subwords)) # max = the most subwords
输出类似:
('greatgrandmothers',
set(['and', 'rand', 'great', 'her', 'mothers', 'moth', 'mother', 'others', 'grandmothers', 'grandmother', 'ran', 'other', 'greatgrandmothers', 'greatgrandmother', 'grand', 'hers', 'the', 'eat']))
来自http://www.mieliestronk.com/wordlist.html的单词列表。
现在我知道你不是为了表演(上面的代码已经<1s,标准英语词汇为58k字)。
但是如果您需要以超快的速度运行,请在某些内循环中说:)
check_dict
内创建所有子串的副本,这是主要的性能杀手。答案 3 :(得分:1)
这会在几秒钟内完成。 “sowpods.txt”有267627个3个或更多字母的单词
如果您使用的是Python2.5或2.6,则需要使用at_least_3 = set(w for w in words if len(w)>=3)
words = open("sowpods.txt").read().split()
at_least_3 = {w for w in words if len(w)>=3}
def count_subwords(word):
counter = 0
for i in range(len(word)-2):
for j in range(i+3,len(word)+1):
candidate = word[i:j]
if candidate in at_least_3:
counter += 1
return counter
for row in sorted((count_subwords(w),w) for w in at_least_3):
print row
子字数最多为26
(26, 'CORESEARCHERS')
(26, 'FOREGONENESSES')
(26, 'METAGENETICALLY')
(26, 'PREPOSSESSIONS')
(26, 'SACRAMENTALISTS')
(26, 'WHOLESOMENESSES')
答案 4 :(得分:0)
这就是你要问的或者我错过了什么?
>>> words = ['a', 'asd', 'asdf', 'bla']
>>> [sum(1 for i in (a for a in words if a in b)) for b in words]
[1, 2, 3, 2]
这是每个单词中的单词数量。如果你不想计算少于3个字符的单词,只需删除它们......
当然,它是O(n²)
编辑:
问题要求所有子词,但代码只询问具有更多子词的子句...如果你真的想要第一个行为,只需删除sum(...)
部分并使genexp成为列表理解...