Como posso encontrar a primeira ocorrência de um padrão em uma string de alguma posição inicial?

StackOverflow https://stackoverflow.com/questions/120071

  •  02-07-2019
  •  | 
  •  

Pergunta

Eu tenho uma seqüência de comprimento arbitrário, e começando na posição p0, eu preciso encontrar a primeira ocorrência de um dos três padrões de 3 letras.

Suponha que a seqüência conter apenas letras. Eu preciso encontrar a contagem de trigêmeos começando na posição p0 e saltar para a frente em trigêmeos até a primeira ocorrência de qualquer 'AAA' ou 'bbb' ou 'CCC'.

É isto mesmo possível usando apenas um regex?

Foi útil?

Solução

Moritz diz que isso pode ser mais rápido do que um regex. Mesmo que seja um pouco mais lento, é mais fácil de entender às 5 da manhã. :)

             #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;
    }

Outras dicas

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

(Assumindo p0 é 0-base).

É claro, é provavelmente mais eficiente usar substr na corda para saltar para a frente:

substr($string, $p0)=~/^(?:...)*?(aaa|bbb|ccc)/;

Você não pode realmente contar com expressões regulares, mas você pode fazer algo como isto:

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;

Mas eu acho que é um pouco mais rápido para uso substr () e descompactar () para dividir em triplo e caminhar os triplos em um loop for.

(edit: É length (), não lenght (); -)

A parte principal é dividida /(...)/. Mas, ao final deste, você terá suas posições e dados de ocorrência.

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

Ou para simples contagem por regex (ele usa Experimental (?? {}))

my ( $count, %count );
my $data_string      
    = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
    ;
$data_string =~ m/(aaa|bbb|ccc)(??{ $count++; $count{$^N}++ })/g;

Se a velocidade é uma preocupação séria, você pode, dependendo do que as 3 cordas são, ficar realmente fantasia, criando uma árvore (por exemplo Aho-Corasick algoritmo ou similar).

Um mapa para cada estado possível é possível, por exemplo, estado [0] [ 'a'] = 0 se sem cordas começam com 'a'.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top