我正在编写正则表达式来检查是否存在子串,其中包含至少2个彼此相邻的模式重复。我将正则表达式的结果与前一个字符串相匹配 - 如果相等,则存在这样的模式。通过示例更好地说:1010包含模式10,并且连续系列中有2次。另一方面,10210将不具有这种模式,因为那些10不相邻。
更重要的是,我需要找到可能的最长模式,并且它的长度至少为1.我已经编写了表达式来检查它^.*?(.+)(\1).*?$
。为了找到最长的模式,我使用了非贪婪的版本来匹配模式之前的东西,然后模式匹配到组1,并再次匹配组1匹配的相同的东西。然后匹配其余的字符串,产生相等的字符串。但是有一个问题是正则表达式在找到第一个模式之后急于返回,并且没有真正考虑到我打算在最短的时间之前和之后制作这些子串(剩下最长的可能)。所以从字符串01011010
我得到了正确的匹配,但是存储在组1中的模式只是01
,尽管我除了101
。
因为我相信我不能使模式“更贪婪”或者在“更多非贪婪”之前和之后的垃圾,我只能想出一个想法让正则表达式不那么渴望,但我不确定这是不是可能。
进一步的例子:
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
使用当前表达式(使用相同的字符串)我会得到什么:
no pattern - no match
pattern 2 - match
pattern 555 - match
pattern 1919 - match
pattern 191919 - match
pattern 23 - match
答案 0 :(得分:12)
在Perl中,您可以在(??{ code })
的帮助下使用一个表达式执行此操作:
$_ = '01011010';
say /(?=(.+)\1)(?!(??{ '.+?(..{' . length($^N) . ',})\1' }))/;
输出:
101
这里发生的事情是,在匹配连续的一对子串之后,我们确保使用负向前瞻,确认不再有一对跟随它。
为了使较长的对的表达式使用推迟的子表达式构造(??{ code })
,它将评估内部的代码(每次)并使用返回的字符串作为表达式。
它构造的子表达式具有.+?(..{N,})\1
形式,其中N是第一个捕获组的当前长度(length($^N)
,$^N
包含前一个捕获组的当前值。
因此,完整表达式将具有以下形式:
(?=(.+)\1)(?!.+?(..{N,})\2}))
使用神奇的N
(和第二个捕获组不是原始表达式的“真实”/正确捕获组)。
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';
输出:
101
10001
191919
101
答案 1 :(得分:1)
您可以在单个正则表达式中执行此操作,您只需手动从结果列表中选择最长匹配。
def longestrepeating(strg):
regex = re.compile(r"(?=(.+)\1)")
matches = regex.findall(strg)
if matches:
return max(matches, key=len)
这给你(因为re.findall()
返回匹配的捕获组列表,即使匹配本身是零长度):
>>> 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 :(得分:0)
正则表达式可以帮助解决这个问题,但我不认为你可以将它作为单个表达式来实现,因为你想要找到最长的成功匹配,而正则表达式只是寻找他们能找到的第一场比赛。可以使用贪婪来调整首先找到哪个匹配(字符串中的早期与后期),但我想不出某种方法可以在之后优先使用更早,更长的子字符串,较短的子字符串,而还更喜欢以后更长的子字符串而不是更早的子字符串。
使用正则表达式的一种方法是按递减顺序迭代可能的长度,并在找到指定长度的匹配后立即退出:
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'
答案 3 :(得分:0)
这是一个长期的脚本,可以满足您的要求。它基本上通过你的输入字符串,缩短一个,然后再次通过它。一旦找到所有可能的匹配,它将返回最长的匹配之一。可以调整它以便返回所有最长的匹配,而不仅仅是一个,但我会留给你。
这是非常基本的代码,但希望你能得到它的要点。
use v5.10;
use strict;
use warnings;
while (<DATA>) {
chomp;
print "$_ : ";
my $longest = foo($_);
if ($longest) {
say $longest;
} else {
say "No matches found";
}
}
sub foo {
my $num = shift;
my @hits;
for my $i (0 .. length($num)) {
my $part = substr $num, $i;
push @hits, $part =~ /(.+)(?=\1)/g;
}
my $long = shift @hits;
for (@hits) {
if (length($long) < length) {
$long = $_;
}
}
return $long;
}
__DATA__
56712453289
22010110100
5555555
1919191919
191919191919
2323191919191919
答案 4 :(得分:0)
不确定是否有人想到这个...
my $originalstring="pdxabababqababqh1234112341";
my $max=int(length($originalstring)/2);
my @result;
foreach my $n (reverse(1..$max)) {
@result=$originalstring=~m/(.{$n})\1/g;
last if @result;
}
print join(",",@result),"\n";
最长的双倍匹配不能超过原始字符串长度的一半,所以我们从那里算起来。
如果相对于原始字符串的长度怀疑匹配较小,那么这个想法可以颠倒...而不是倒数直到我们找到匹配,我们计算直到没有更多的匹配。然后我们需要备份1并给出结果。我们还需要在正则表达式中的$ n之后加一个逗号。
my $n;
foreach (1..$max) {
unless (@result=$originalstring=~m/(.{$_,})\1/g) {
$n=--$_;
last;
}
}
@result=$originalstring=~m/(.{$n})\1/g;
print join(",",@result),"\n";