如何找到多个字符串中最长的公共子字符串?

我正在编写一个有多个字符串的python脚本。

例如:

x = "brownasdfoersjumps"

y = "foxsxzxasis12sa[[#brown"

z = "thissasbrownxc-34a@s;"

在这三个字符串中,它们共有一个子字符串,即brown。我想以一种创建字典的方式来搜索它:

dict = {[commonly occuring substring] => 

[total number of occurrences in the strings provided]}

最好的方法是什么?考虑到我每次都会有200多个字符串,那么简单/有效的方式是什么呢?

回答:

这是一个相对优化的朴素算法。首先,将每个序列转换为所有ngram的集合。然后,将所有集合相交,并在相交中找到最长的ngram。

from functools import partial, reduce

from itertools import chain

from typing import Iterator

def ngram(seq: str, n: int) -> Iterator[str]:

return (seq[i: i+n] for i in range(0, len(seq)-n+1))

def allngram(seq: str) -> set:

lengths = range(len(seq))

ngrams = map(partial(ngram, seq), lengths)

return set(chain.from_iterable(ngrams))

sequences = ["brownasdfoersjumps",

"foxsxzxasis12sa[[#brown",

"thissasbrownxc-34a@s;"]

seqs_ngrams = map(allngram, sequences)

intersection = reduce(set.intersection, seqs_ngrams)

longest = max(intersection, key=len) # -> brown

虽然这可能使您了解短序列,但此算法在长序列上效率极低。如果序列很长,则可以添加启发式方法以限制最大可能的ngram长度(即,可能的最长公共子串)。这种启发式方法的一个显而易见的价值可能是最短序列的长度。

def allngram(seq: str, minn=1, maxn=None) -> Iterator[str]:

lengths = range(minn, maxn) if maxn else range(minn, len(seq))

ngrams = map(partial(ngram, seq), lengths)

return set(chain.from_iterable(ngrams))

sequences = ["brownasdfoersjumps",

"foxsxzxasis12sa[[#brown",

"thissasbrownxc-34a@s;"]

maxn = min(map(len, sequences))

seqs_ngrams = map(partial(allngram, maxn=maxn), sequences)

intersection = reduce(set.intersection, seqs_ngrams)

longest = max(intersection, key=len) # -> brown

这可能仍会花费太长时间(或使您的计算机用完RAM),因此您可能需要阅读一些最佳算法(请参阅我在评论中留给您的问题的链接)。

计算每个ngram出现的字符串数

from collections import Counter

sequences = ["brownasdfoersjumps",

"foxsxzxasis12sa[[#brown",

"thissasbrownxc-34a@s;"]

seqs_ngrams = map(allngram, sequences)

counts = Counter(chain.from_iterable(seqs_ngrams))

Counter是的子类dict,因此其实例具有相似的接口:

print(counts)

Counter({'#': 1,

'#b': 1,

'#br': 1,

'#bro': 1,

'#brow': 1,

'#brown': 1,

'-': 1,

'-3': 1,

'-34': 1,

'-34a': 1,

'-34a@': 1,

'-34a@s': 1,

'-34a@s;': 1,

...

您可以过滤计数以使子字符串至少出现在n字符串中:{string: count for string, count in counts.items()

if count >= n}

以上是 如何找到多个字符串中最长的公共子字符串? 的全部内容, 来源链接: utcz.com/qa/407794.html

回到顶部