Как я могу найти первое вхождение шаблона в строку из некоторой начальной позиции? - PullRequest
6 голосов
/ 23 сентября 2008

У меня есть строка произвольной длины, и, начиная с позиции p0, мне нужно найти первое вхождение одного из трех трехбуквенных шаблонов.

Предположим, что строка содержит только буквы. Мне нужно найти количество триплетов, начиная с позиции p0 и прыгая вперед в триплетах до первого появления «aaa», «bbb» или «ccc».

Возможно ли это даже с помощью регулярного выражения?

Ответы [ 5 ]

12 голосов
/ 23 сентября 2008

Мориц говорит, что это может быть быстрее, чем регулярное выражение. Даже если это немного медленнее, это легче понять в 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;
    }
12 голосов
/ 23 сентября 2008
$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)/;
9 голосов
/ 23 сентября 2008

Вы не можете действительно считать с помощью регулярных выражений, но вы можете сделать что-то вроде этого:

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.

(редактировать: это длина (), а не длина (); -)

0 голосов
/ 08 ноября 2008

Если скорость представляет собой серьезную проблему, вы можете, в зависимости от того, что представляют собой 3 строки, по-настоящему модно создать дерево (например, алгоритм Aho-Corasick или аналогичный).

Возможна карта для каждого возможного состояния, например, состояние [0] ['a'] = 0, если строки не начинаются с 'a'.

0 голосов
/ 23 сентября 2008

Основная часть этого разделена /(...)/. Но в конце этого у вас будут данные о ваших позициях и происшествиях.

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;
...