如何从某个起始位置查找字符串中模式的首次出现?
时间:2020-03-06 14:35:23 来源:igfitidea点击:
我有一个任意长度的字符串,并且从位置p0开始,我需要找到三个3字母模式之一的第一次出现。
假设字符串仅包含字母。我需要找到从位置p0开始并在三元组中向前跳跃直到第一次出现" aaa"或者" bbb"或者" ccc"的三元组计数。
仅使用正则表达式是否有可能?
解决方案
$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 /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() / 3;
但是我认为使用substr()和unpack()拆分为三元组并在for循环中遍历三元组会更快一些。
(编辑:它是length()而不是lenght();-)
莫里茨说,这可能比正则表达式要快。即使稍微慢一点,在凌晨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 at $pos\n"; last; } $pos += $length; }
它的主要部分是分割/(...)/。但是最后,我们将获得自己的位置和发生数据。
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}; }
或者通过正则表达式进行简单计数(它使用实验性(?? {}))
my ( $count, %count ); my $data_string = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan' ; $data_string =~ m/(aaa|bbb|ccc)(??{ $count++; $count{$^N}++ })/g;
如果速度是一个非常重要的问题,我们可以根据3个字符串的含义,通过创建一棵树来实现真正的幻想(例如Aho-Corasick算法或者类似算法)。
每个可能状态的映射都是可能的,例如如果没有字符串以" a"开头,则state [0] ['a'] = 0。