最短重复子串

时间:2011-12-26 08:07:20

标签: python regex string-matching

我正在寻找一种有效的方法来提取最短的重复子串。 例如:

input1 = 'dabcdbcdbcdd'
ouput1 = 'bcd'

input2 = 'cbabababac'
output2 = 'ba'

我希望得到与此问题相关的任何答案或信息。

此外,在this post中,人们建议我们可以使用正则表达式,如

re=^(.*?)\1+$

找到字符串中最小的重复模式。但是这样的表达式在Python中不起作用并且总是让我不匹配(我是Python的新手,也许我想念一些东西?)。

---跟进---

这里的标准是寻找长度大于1并且总长度最长的最短非重叠模式。

2 个答案:

答案 0 :(得分:14)

此模式的快速修复可能是

(.+?)\1+

你的正则表达式失败了,因为它将重复的字符串锚定到行的开头和结尾,只允许abcabcabc而不是xabcabcabcx这样的字符串。此外,重复字符串的最小长度应为1,而不是0(或任何字符串匹配),因此.+?而不是.*?

在Python中:

>>> import re
>>> r = re.compile(r"(.+?)\1+")
>>> r.findall("cbabababac")
['ba']
>>> r.findall("dabcdbcdbcdd")
['bcd']

但请注意,此正则表达式只会找到非重叠的重复匹配,因此在上一个示例中,虽然这是最短的重复字符串,但找不到解d。或者查看此示例:此处找不到abcd,因为第一个abc的{​​{1}}部分已在第一场比赛中用完了):

abcd

此外,它可能会返回多个匹配项,因此您需要在第二步中找到最短的匹配项:

>>> r.findall("abcabcdabcd")
['abc']

更好的解决方案:

要让引擎找到重叠的匹配项,请使用

>>> r.findall("abcdabcdabcabc")
['abcd', 'abc']

这会发现一些字符串两次或更多,如果它们重复了足够多次,但它肯定会找到所有可能的重复子字符串:

(.+?)(?=\1)

因此,您应该按长度对结果进行排序并返回最短的结果:

>>> r = re.compile(r"(.+?)(?=\1)")
>>> r.findall("dabcdbcdbcdd")
['bcd', 'bcd', 'd']

>>> min(r.findall("dabcdbcdbcdd") or [""], key=len) 'd' (感谢J. F. Sebastian!)确保如果根本没有匹配则不会触发or [""]

答案 1 :(得分:3)

^匹配字符串的开头。在您的示例中,重复的子字符串不会从头开始。类似于$。如果没有^$,则模式.*?始终匹配空字符串。 Demo

import re

def srp(s):
    return re.search(r'(.+?)\1+', s).group(1)

print srp('dabcdbcdbcdd') # -> bcd
print srp('cbabababac')   # -> ba

虽然找不到最短的子串。