【发布时间】:2020-11-27 09:06:14
【问题描述】:
给定一个长字符串,找出最长的重复子字符串。
蛮力方法当然是查找所有子字符串并检查剩余字符串的子字符串,但有问题的字符串有数百万个字符(如 DNA 序列、AGGCTAGCT 等),我想在宇宙自行坍塌之前完成的事情。
尝试了多种方法,我有一个解决方案可以在高达数百万的字符串上运行得非常快,但对于较大的字符串实际上需要永远(6 小时以上),特别是当重复序列的长度变得非常长时.
def find_lrs(text, cntr=2):
sol = (0, 0, 0)
del_list = ['01','01','01']
while len(del_list) != 0:
d = defaultdict(list)
for i in range(len(text)):
d[text[i:i + cntr]].append(i)
del_list = [(item, d[item]) for item in d if len(d[item]) > 1]
# if list is empty, we're done
if len(del_list) == 0:
return sol
else:
sol = (del_list[0][1][0], (del_list[0][1][1]),len(del_list[0][0]))
cntr += 1
return sol
我知道这很难看,但是嘿,我是一个初学者,我很高兴我有一些工作要做。想法是遍历以长度为 2 的子字符串作为键开始的字符串,子字符串的索引位于该值处。如果文本是“BANANA”,那么在第一次通过后,dict 将如下所示:
{'BA': [0], 'AN': [1, 3], 'NA': [2, 4], 'A': [5]}
BA 只出现一次 - 从索引 0 开始。AN 和 NA 出现两次,分别出现在索引 1/3 和 2/4。
然后我创建一个列表,其中仅包含至少出现两次的键。在上面的示例中,我们可以删除 BA,因为它只出现一次 - 如果没有以 'BA' 开头的长度为 2 的子字符串,则不会有以 BA 开头的长度为 3 的子字符串。 所以经过剪枝后的第一个过去是: [('AN', [1, 3]), ('NA', [2, 4])]
由于至少有两种可能性,我们保存迄今为止找到的最长子字符串和索引,并将子字符串长度增加到 3。我们继续直到没有重复子字符串。
如上所述,这可以在大约 2 分钟内处理多达 1000 万个字符串,这显然是合理的 - 但是,最长的重复序列相当短。在较短的字符串但较长的重复序列上,运行需要-小时-。我怀疑这与字典的大小有关,但不太清楚为什么。
我当然想做的是通过删除明显不重复的子字符串来保持字典的简短,但是在迭代它时我不能从字典中删除项目。我知道有后缀树方法,而且现在 - 不在我的范围内。
可能只是这超出了我目前的知识范围,这当然很好,但我不禁动摇了这里有解决方案的想法。
【问题讨论】:
-
子串能否重叠自身,例如:'ANA'在'BANANA'中有两次出现
-
这似乎是计算机科学中一个经过充分研究的问题:en.wikipedia.org/wiki/Longest_repeated_substring_problem
-
感觉可以通过跟踪子字符串出现的位置来减少搜索空间。如果你在 3 个地方找到一个长度为 N 的重复子串,你只需要检查这 3 个地方的 N+1 个长度的字符串,对吧?在我看来,它们不可能在其他任何地方发生。还是我弄错了?
-
是的,子字符串可以重叠。 BANANA中最长的子串是ANA,长度为3,从[1]和[3]开始。
-
这能回答你的问题吗? Find longest repetitive sequence in a string