Python-查找可以在单词内部找到的所有子单词
最后,我想找出英语词典中哪一个词包含的子词最多,至少有三个字母。我写了这个算法,但它太慢了,没有用处。想知道如何优化它吗Python-查找可以在单词内部找到的所有子单词,python,algorithm,Python,Algorithm,最后,我想找出英语词典中哪一个词包含的子词最多,至少有三个字母。我写了这个算法,但它太慢了,没有用处。想知道如何优化它吗 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
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()
这就是你要问的还是我遗漏了什么
>>> 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成为一个列表理解…1)样式和组织:使用一个函数生成一个单词的所有子单词更有意义
2) 样式:使用set
不需要双括号
3) 性能(我希望):也从正在查找的单词中设置一个set
;然后可以使用内置的集合交点检查
4) 性能(几乎可以肯定):不要手动循环以找到最大元素;使用内置的max
。您可以直接比较(长度、元素)元组;Python从头到尾按每对元素比较元组,就像每个元素都是字符串中的字母一样
5) 性能(可能):确保字典中没有一个字母或两个字母的单词作为开头,因为它们只是碍事
6) 性能(可悲的事实):不要将一切分解为一个函数
7) 样式:文件I/O应该使用带有块的,以确保正确清理资源,默认情况下,文件迭代器迭代行,因此我们可以隐式获得行列表,而不必调用.readlines()
我最终得到了(除了“片段”表达式外,没有正确测试):
您的算法的主要缺点是,对于每个子词,您需要将其与字典中的每个其他词进行比较。你真的不需要这样做——如果你的单词以“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是字典中的单词数。这将在几秒钟内运行。“sowpods.txt”包含3个或更多字母的267627个单词
如果您使用的是Python2.5或2.6,那么您需要使用至少\u 3=set(如果len(w)>=3,则w表示w)
子字的最大数目是26
(26, 'CORESEARCHERS')
(26, 'FOREGONENESSES')
(26, 'METAGENETICALLY')
(26, 'PREPOSSESSIONS')
(26, 'SACRAMENTALISTS')
(26, 'WHOLESOMENESSES')
要探索基本Python,请看一下这个函数(基本上是JBernardo和Karl Knechtel建议的更快、更完善、更令人满意的版本):
输出类似于:
('greatgrandmothers',
set(['and', 'rand', 'great', 'her', 'mothers', 'moth', 'mother', 'others', 'grandmothers', 'grandmother', 'ran', 'other', 'greatgrandmothers', 'greatgrandmother', 'grand', 'hers', 'the', 'eat']))
对于来自的单词列表
现在我知道你不是在追求性能(上面的代码是你已经在任何地方使用集合了,除了它的作用:如果dictList中的单词
应该是如果dictSet中的单词
顺便说一句,你的描述是“三个字母,”你的代码是2。因为我甚至不知道那东西是做什么的。@hop那么你应该学一点python…sum(1代表i in…
非常常见,意思是len(iterable)
我对这个习语非常熟悉。这个答案并没有告诉我们什么是好的编码风格,也没有试图弄清楚发生了什么,也没有试图遵循OP的代码,可能也不会很快解决问题。这是Waaaay卓越的答案!这非常有效!谢谢你提供的所有提示。另外,你能解释一下如何解决问题吗代码的片段部分是有效的。在一个集合中如何有for循环?这是生成器理解吗?为什么选择使用range(i+3)?这是一个生成器理解,在两个计数器上迭代。j
从i+3
开始,因为i
标记单词的开头,而j
标记单词的结尾。挑剔:这是一个生成器表达式,与列表、集合或字典理解相反。生成器作为迭代器访问,而编译器作为迭代器访问nsion只是构造容器的一种特殊语法。算法不错,但我敢打赌它对python来说太低级了。在set
s中查找时,可能会出错太多,python可能会更快。不知道您的代码是否有效,但对于手绘图片Azing图片和解释+1
(26, 'CORESEARCHERS')
(26, 'FOREGONENESSES')
(26, 'METAGENETICALLY')
(26, 'PREPOSSESSIONS')
(26, 'SACRAMENTALISTS')
(26, 'WHOLESOMENESSES')
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']))