使用树快速检查字符串是否包含大型列表中的元素
Check if a string contains an elemewnt in a large list quickly using a tree
我有一大堆短字符串(单词),我想检查它们是否出现在另一个字符串(句子)中。请注意,我不关心实际的 words/spaces/punctuation/etc.
这是python中的典型解决方案:
def contains_one_of(sentence, words):
for word in words:
if word in sentence:
return word
return None
我见过一些 python 单行代码做同样的事情,但从算法上讲,我能找到的所有内容似乎基本上都是为所有元素调用一个 contains 函数。我假设 contains 函数使用一种滑动 window 方法。
我估计的复杂度大约是 O(nmo)
其中 n = 列表长度,m = 句子长度,o = 列表中单词的平均长度
对我来说,我认为这可以用树来改进,但我找不到任何关于这种算法的参考。 我基本上设想单词数组变成一棵树,其中一个节点是一个字母,它的所有子节点都是该单词的下一个字母。只要单词短而且有合理的重叠我认为这样会更有效率。
我已经在 python 中实现了这个版本,但我更愿意使用一个利用 C 来比较所有这些字符的包。 如果您知道此算法的名称或执行此操作的程序包,我很想知道。
这是我的版本,我相信很多地方都可以优化,但我想知道我是否对这里有所了解。
sentence = "hello there cat, welcome home"
words = ["cat", "car", "cam", "arm", "ace", "arc"]
# build a dict tree per letter
def build_tree(patterns):
root = dict()
for p in patterns:
r = root
for i, c in enumerate(p):
if c not in r:
if i >= len(p) - 1: # last element
r[c] = p
else: # any other element
r[c] = dict()
r = r[c]
return root
# Check if the substring starts with a path through the tree
def starts_with_tree(sub, tree):
level = tree
for c in sub:
if c not in level: # nowhere left to go
return None
elif isinstance(level[c], str): # if we found a string we are at the end
return level[c]
else:
level = level[c] # go deeper
# Check if s contains any path through the tree
def contains_which(s, root):
for i in range(len(s)):
sub = s[i:] # A substring missing the first i characters
result = starts_with_tree(sub, root)
if result:
return result
return None
# build the tree
tree_root = build_tree(words)
print(tree_root)
# search within tree
found = contains_which(sentence, tree_root)
print("Found:", found)
您可以使用 aho-corasick 算法。
它使用 trie 结构(一种树),时间复杂度只是 O(m + o*n)(根据您的定义)(线性时间复杂度与长度和所有字符串)
如果你不熟悉这个算法,这个算法的实现是相当复杂的。所以你可以使用 python 库来获得 aho-corasick pyahocorasick
更多详情
我有一大堆短字符串(单词),我想检查它们是否出现在另一个字符串(句子)中。请注意,我不关心实际的 words/spaces/punctuation/etc.
这是python中的典型解决方案:
def contains_one_of(sentence, words):
for word in words:
if word in sentence:
return word
return None
我见过一些 python 单行代码做同样的事情,但从算法上讲,我能找到的所有内容似乎基本上都是为所有元素调用一个 contains 函数。我假设 contains 函数使用一种滑动 window 方法。
我估计的复杂度大约是 O(nmo)
其中 n = 列表长度,m = 句子长度,o = 列表中单词的平均长度
对我来说,我认为这可以用树来改进,但我找不到任何关于这种算法的参考。 我基本上设想单词数组变成一棵树,其中一个节点是一个字母,它的所有子节点都是该单词的下一个字母。只要单词短而且有合理的重叠我认为这样会更有效率。
我已经在 python 中实现了这个版本,但我更愿意使用一个利用 C 来比较所有这些字符的包。 如果您知道此算法的名称或执行此操作的程序包,我很想知道。
这是我的版本,我相信很多地方都可以优化,但我想知道我是否对这里有所了解。
sentence = "hello there cat, welcome home"
words = ["cat", "car", "cam", "arm", "ace", "arc"]
# build a dict tree per letter
def build_tree(patterns):
root = dict()
for p in patterns:
r = root
for i, c in enumerate(p):
if c not in r:
if i >= len(p) - 1: # last element
r[c] = p
else: # any other element
r[c] = dict()
r = r[c]
return root
# Check if the substring starts with a path through the tree
def starts_with_tree(sub, tree):
level = tree
for c in sub:
if c not in level: # nowhere left to go
return None
elif isinstance(level[c], str): # if we found a string we are at the end
return level[c]
else:
level = level[c] # go deeper
# Check if s contains any path through the tree
def contains_which(s, root):
for i in range(len(s)):
sub = s[i:] # A substring missing the first i characters
result = starts_with_tree(sub, root)
if result:
return result
return None
# build the tree
tree_root = build_tree(words)
print(tree_root)
# search within tree
found = contains_which(sentence, tree_root)
print("Found:", found)
您可以使用 aho-corasick 算法。
它使用 trie 结构(一种树),时间复杂度只是 O(m + o*n)(根据您的定义)(线性时间复杂度与长度和所有字符串)
如果你不熟悉这个算法,这个算法的实现是相当复杂的。所以你可以使用 python 库来获得 aho-corasick pyahocorasick
更多详情