Regex 匹配最长的重复序列(不是由重复序列组成)

Regex 匹配最长的重复序列(不是由重复序列组成),regex,Regex,以字符串为例 aabaabaabaabaab 显然,这个字符串由5个相邻的aab组成,因此我希望我的正则表达式与aab匹配 详细说明:aabaab不是可接受的输出,因为它是通过重复aab生成的。但是aab是一个有效的结果,因为它不是由重复的较短字符串组成的 为了回答这个问题,让我们假设重复段周围可能有额外的文本(例如11aabaabababa2甚至xaabaa)。因此,不可能使用^或$锚定正则表达式 失败的想法#1:(.+?)\1+这捕获的是aa,而不是预期的aab 失败的想法#2:(.+)

以字符串为例

aabaabaabaabaab
显然,这个字符串由5个相邻的
aab
组成,因此我希望我的正则表达式与
aab
匹配

详细说明:
aabaab
不是可接受的输出,因为它是通过重复
aab
生成的。但是
aab
是一个有效的结果,因为它不是由重复的较短字符串组成的

为了回答这个问题,让我们假设重复段周围可能有额外的文本(例如
11aabaabababa2
甚至
xaabaa
)。因此,不可能使用
^
$
锚定正则表达式


失败的想法#1:
(.+?)\1+
这捕获的是
aa
,而不是预期的
aab

失败的想法#2:
(.+)\1+
这捕获了
aabaab


用纯正则表达式可以做到这一点吗?如果是,是否可以不使用动态宽度查找?

您可以使用两个查找头,第一个查找最长的图案,第二个查找最小的图案。重复的第二个图案必须(至少)在相同的位置或在重复的第一个图案之后结束。要检查这一点,您必须在第一个前瞻中捕获字符串的结尾,并在第二个前瞻中使用对此捕获的反向引用

def largest_pattern(value)
  /(.+)\1+/.match(value).try("[]", 1)
end

def smallest_pattern(value)
  /^(.+?)\1+$/.match(value).try("[]", 1)
end

def largest_distinct_pattern(value)
  val = largest_pattern(value)
  if val
    while(new_val = smallest_pattern(val))
      val = new_val
    end
    val
  else
    nil
  end
end

largest_distinct_pattern("aabaabaabaabaab")
=> "aab"
(?=(.+)\1+(.*))(?=(.+?)\3+\2$)\3+

结果在第3组

此外:


请注意,这两个正则表达式模式给出了字符串中一个位置的结果。如果你想知道什么是最短的模式,是对所有字符串重复的最长的子字符串,你必须找到所有的模式,并用代码选择最长的子字符串。您可以使用重叠结果的模式来执行此操作:

(?=(.+)\1+(.*))(?=(.+?)\3+\2$)(?=(\3+))

(使用组4)

注意,如果添加锚定,则会得到正确的结果:
^(+.+)\1+$
@casimirithippolyte Good point。但是让我们假设在字符串的开头或结尾可以有更多的数据。我将更新问题。您可以递归地执行此操作,第一次执行
(.+)\1+
,然后每次执行
^(.+?)\1+$
,直到您没有匹配为止,当您没有匹配时,最后尝试的值就是您想要的值。听起来很难。这个字符串也由两个相邻的
aabaab
组成。如果我们可以进行前瞻,
(.+)(?=\1)
以@MichaelGorman描述的方式多次运行,其工作方式与此相同。如果存在多个重复序列,其中最大重复模式不是最大序列,则此操作将失败。例如
abcabc121212
。重复序列的最大模式是
12
,但最小模式的
abc
更长。对不起,我正在寻找一种无代码、纯正则表达式的解决方案。是的,Casimir et Hippolyte的答案无论如何都是一种更干净的解决方案。@MichaelGorman:谢谢,但我真的不相信我的解决方案是干净或有效的。对我来说,这类问题更像是一个编程难题。(用正则表达式做那些看起来不可能的事情)使用额外的
(.*)
组来确保两个匹配都在同一个位置结束是天才+我非常尊敬你!
(?=(.+)\1+(.*))(?=(.+?)\3+\2$)(?=(\3+))