我有一个任意长度的字符串,从位置 p0 开始,我需要找到三个 3 字母模式之一的第一次出现。
假设字符串只包含字母。我需要找到从位置 p0 开始并在三胞胎中向前跳跃直到第一次出现 'aaa' 或 'bbb' 或 'ccc' 的三胞胎的数量。
这甚至可以仅使用正则表达式吗?
Moritz 说这可能比正则表达式更快。哪怕慢一点,早上5点还是比较容易理解的。 :)
#0123456789.123456789.123456789.
my $string = "alsdhfaaasccclaaaagalkfgblkgbklfs";
my $pos = 9;
my $length = 3;
my $regex = qr/^(aaa|bbb|ccc)/;
while( $pos < length $string ) {
print "Checking $pos\n";
if( substr( $string, $pos, $length ) =~ /$regex/ ) {
print "Found $1 at $pos\n";
last;
}
$pos += $length;
}
$string=~/^ # from the start of the string
(?:.{$p0}) # skip (don't capture) "$p0" occurrences of any character
(?:...)*? # skip 3 characters at a time,
# as few times as possible (non-greedy)
(aaa|bbb|ccc) # capture aaa or bbb or ccc as $1
/x;
(假设 p0 从 0 开始)。
当然,在字符串上使用substr向前跳转可能效率更高:
substr($string, $p0)=~/^(?:...)*?(aaa|bbb|ccc)/;
你不能用正则表达式来计算,但你可以这样做:
pos $string = $start_from;
$string =~ m/\G # anchor to previous pos()
((?:...)*?) # capture everything up to the match
(aaa|bbb|ccc)
/xs or die "No match"
my $result = length($1) / 3;
但我认为使用 substr() 和 unpack() 拆分成三元组并在 for 循环中遍历三元组要快一些。
(编辑:它是 length(),不是 lenght() ;-)
这个的主要部分是split /(...)/。但在这结束时,您将获得您的位置和发生数据。
my @expected_triplets = qw<aaa bbb ccc>;
my $data_string
= 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
;
my $place = 0;
my @triplets = grep { length } split /(...)/, $data_string;
my %occurrence_for = map { $_, [] } @expected_triplets;
foreach my $i ( 0..@triplets ) {
my $triplet = $triplets[$i];
push( @{$occurrence_for{$triplet}}, $i ) if exists $occurrence_for{$triplet};
}
或者通过正则表达式进行简单计数(它使用 Experimental (??{}))
my ( $count, %count );
my $data_string
= 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
;
$data_string =~ m/(aaa|bbb|ccc)(??{ $count++; $count{$^N}++ })/g;
如果速度是一个严重的问题,您可以根据 3 个字符串是什么,通过创建树(例如 Aho-Corasick 算法或类似算法)来获得真正的幻想。
每个可能状态的地图都是可能的,例如state[0]['a'] = 0 如果没有字符串以 'a' 开头。