¿Cómo puedo encontrar la primera aparición de un patrón en una cadena de cierta posición de partida?

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

  •  02-07-2019
  •  | 
  •  

Pregunta

Tengo una cadena de longitud arbitraria, y empezando en la posición p0, necesito encontrar la primera aparición de uno de los tres 3-patrones de letras.

Suponga que la cadena de contener sólo letras.Necesito encontrar el recuento de los trillizos de partida en la posición p0 y saltar hacia adelante en trillizos hasta la primera ocurrencia de 'aaa' o 'bbb' o 'ccc'.

Esto es incluso posible utilizar sólo una expresión regular?

¿Fue útil?

Solución

Moritz dice que esto puede ser más rápido que una expresión regular.Incluso si es un poco más lento, es más fácil entender a las 5 am.:)

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

Otros consejos

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

(Suponiendo que p0 es basado en 0).

Por supuesto, es probable que sea más eficiente usar substr en la cuerda para saltar hacia adelante:

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

Realmente no se puede contar con expresiones regulares, pero se puede hacer algo como esto:

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;

Pero creo que es un poco más rápido de usar substr() y unpack() para dividir en triples y caminar por la triplica en un bucle.

(edición:es la longitud(), no lenght() ;-)

La parte principal de este es el split /(...)/.Pero al final de esto, usted tendrá sus posiciones y la ocurrencia de datos.

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

O por simple conteo de regex (utiliza Experimental (??{}))

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

Si la velocidad es un problema grave, puede, dependiendo de lo que el 3 cadenas, conseguir realmente de lujo mediante la creación de un árbol (por ejemplo,Algoritmo de Aho-Corasick o similar).

Un mapa para cada estado posible es posible, por ejemplo,estado[0]['a'] = 0 si no hay cadenas que comienzan con 'un'.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top