首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >匹配最长重复子字符串的正则表达式

匹配最长重复子字符串的正则表达式
EN

Stack Overflow用户
提问于 2012-02-10 02:41:01
回答 5查看 7.1K关注 0票数 14

我正在编写正则表达式来检查是否有一个子串,它包含至少两个相邻的模式的重复。我将正则表达式的结果与以前的字符串进行匹配-如果相等,则存在这样的模式。更好的说法是: 1010包含模式10,并且它在连续序列中存在2次。另一方面,10210不会有这样的模式,因为这10个不相邻。

更重要的是,我需要找到可能的最长模式,它的长度至少是1。我已经编写了表达式来检查它的^.*?(.+)(\1).*?$。为了找到最长的模式,我在模式之前使用了非贪婪的版本来匹配一些东西,然后模式被匹配到组1,并且再次匹配已经为group1匹配的相同的东西。然后匹配字符串的其余部分,生成相等的字符串。但是有一个问题,正则表达式在找到第一个模式后急于返回,并且没有真正考虑到我打算在最短字符串之前和之后设置这些子字符串(让其余的字符串尽可能长)。所以从字符串01011010中我正确地得到了匹配,但是存储在组1中的模式只是01,尽管我排除了101

因为我相信我不能让模式“更贪婪”或者在“更不贪婪”之前和之后变得垃圾,我只能想出一个想法来让正则表达式不那么急切,但我不确定这是否可能。

更多示例:

代码语言:javascript
复制
56712453289 - no pattern - no match with former string
22010110100 - pattern 101 - match with former string (regex resulted in 22010110100 with 101 in group 1)
5555555 - pattern 555 - match
1919191919 - pattern 1919 - match
191919191919 - pattern 191919 - match
2323191919191919 - pattern 191919 - match

我使用current表达式会得到什么(使用相同的字符串):

代码语言:javascript
复制
no pattern - no match
pattern 2 - match
pattern 555 - match
pattern 1919 - match
pattern 191919 - match
pattern 23 - match
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-02-10 03:10:42

在Perl中,你可以在(??{ code })的帮助下用一个表达式完成这项工作。

代码语言:javascript
复制
$_ = '01011010';
say /(?=(.+)\1)(?!(??{ '.+?(..{' . length($^N) . ',})\1' }))/;

输出:

代码语言:javascript
复制
101

这里发生的情况是,在匹配连续的子字符串对之后,我们通过负向先行确保不再有后续的子字符串对。

为了使较长的表达式对更长,使用了一个延迟的子表达式构造(??{ code }),它(每次)计算内部的代码,并使用返回的字符串作为表达式。

它构造的子表达式的形式为.+?(..{N,})\1,其中N是第一个捕获组的当前长度(length($^N)$^N包含前一个捕获组的当前值)。

因此,完整的表达式将具有以下形式:

代码语言:javascript
复制
(?=(.+)\1)(?!.+?(..{N,})\2}))

其中神奇的N (并且第二个捕获组不是原始表达式的“真正”/proper捕获组)。

Usage example

代码语言:javascript
复制
use v5.10;

sub longest_rep{
    $_[0] =~ /(?=(.+)\1)(?!(??{ '.+?(..{' . length($^N) . ',})\1' }))/;
}

say longest_rep '01011010';
say longest_rep '010110101000110001';
say longest_rep '2323191919191919';
say longest_rep '22010110100';

输出:

代码语言:javascript
复制
101
10001
191919
101
票数 14
EN

Stack Overflow用户

发布于 2012-02-10 02:54:22

您可以在单个正则表达式中完成此操作,您只需手动从结果列表中选择最长的匹配即可。

代码语言:javascript
复制
def longestrepeating(strg):
    regex = re.compile(r"(?=(.+)\1)")
    matches = regex.findall(strg)
    if matches:
        return max(matches, key=len)

这将为您提供(因为re.findall()会返回匹配捕获组的列表,即使匹配本身长度为零):

代码语言:javascript
复制
>>> longestrepeating("yabyababyab")
'abyab'
>>> longestrepeating("10100101")
'010'
>>> strings = ["56712453289", "22010110100", "5555555", "1919191919", 
               "191919191919", "2323191919191919"]
>>> [longestrepeating(s) for s in strings]
[None, '101', '555', '1919', '191919', '191919']
票数 2
EN

Stack Overflow用户

发布于 2012-02-10 03:02:34

正则表达式可以帮助解决这个问题,但我不认为您可以将其作为单个表达式来完成,因为您希望找到最长的成功匹配,而正则表达式只查找它们可以找到的第一个匹配。贪婪可以用来调整首先找到的匹配(字符串中的更早和更晚),但我想不出一种方法来更喜欢较早的、较长的子串而不是较晚的、较短的子串,同时也更喜欢较晚的、较长的子串而不是较早的、较短的子串。

使用正则表达式的一种方法是按降序迭代可能的长度,并在找到指定长度的匹配项时立即退出:

代码语言:javascript
复制
my $s = '01011010';
my $one = undef;
for(my $i = int (length($s) / 2); $i > 0; --$i)
{
  if($s =~ m/(.{$i})\1/)
  {
    $one = $1;
    last;
  }
}
# now $one is '101'
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9216783

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档