Come posso trovare la prima occorrenza di un pattern in una stringa da una posizione iniziale?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Ho una stringa di lunghezza arbitraria e, partendo dalla posizione p0, devo trovare la prima occorrenza di uno dei tre schemi di 3 lettere.

Supponiamo che la stringa contenga solo lettere. Devo trovare il conteggio delle terzine che iniziano nella posizione p0 e saltano avanti nelle terzine fino alla prima occorrenza di "aaa" o "bbb" o "ccc".

È anche possibile usare solo una regex?

È stato utile?

Soluzione

Moritz dice che potrebbe essere più veloce di una regex. Anche se è un po 'più lento, è più facile da capire alle 5 del mattino. :)

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

Altri suggerimenti

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

(Supponendo che p0 sia basato su 0).

Ovviamente, è probabilmente più efficiente usare substr sulla stringa per saltare in avanti:

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

Non puoi davvero contare sulle regex, ma puoi fare qualcosa del genere:

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;

Ma penso che sia un po 'più veloce usare substr () e unpack () per dividere in triple e camminare le triple in un for-loop.

(modifica: è lunghezza (), non lunghezza () ;-)

La parte principale di questo è divisa /(...)/. Ma alla fine, avrai le tue posizioni e i dati di occorrenza.

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 { 

La parte principale di questo è divisa /(...)/. Ma alla fine, avrai le tue posizioni e i dati di occorrenza.

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

O per un semplice conteggio da regex (usa Experimental (?? {}))

<*>, [] } @expected_triplets; foreach my $i ( 0..@triplets ) { my $triplet = $triplets[$i]; push( @{$occurrence_for{$triplet}}, $i ) if exists $occurrence_for{$triplet}; }

O per un semplice conteggio da regex (usa Experimental (?? {}))

<*>

Se la velocità è una preoccupazione seria, puoi, a seconda di cosa sono le 3 stringhe, diventare davvero fantasiosi creando un albero (ad esempio algoritmo Aho-Corasick o simile).

È possibile una mappa per ogni stato possibile, ad es. state [0] ['a'] = 0 se nessuna stringa inizia con 'a'.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top