2012-03-02 22 views
32

Qual è il modo più veloce per contare il numero di volte in cui una determinata stringa appare in una più grande? La mia ipotesi migliore sarebbe quella di sostituire tutte le istanze di quella stringa senza niente, calcolare la differenza di lunghezze e dividere per la lunghezza della sottostringa, ma ciò sembra piuttosto lento e ho bisogno di analizzare grandi quantità di dati.Numero di conteggi di una stringa all'interno di un'altra (Perl)

+0

potrebbe voler controllare questo fuori ... anche se è dal 1999, e ci sono molto probabilmente altri modi per fare questo genere di cose efficientemente: http://www.perlmonks.org/?node=How%20can%20I%20count%20the%20number%20of%20occurrences%20of%20a%20substring%20within%20a%20string%3F – summea

+7

'perldoc -q count ' – toolic

+2

Si possono sovrapporre? – tchrist

risposta

57

È possibile acquisire le stringhe, quindi contarle. Esso può essere fatto mediante l'applicazione di un contesto di lista alla cattura con ():

my $x = "foo"; 
my $y = "foo foo foo bar"; 
my $c =() = $y =~ /$x/g; # $c is now 3 

È possibile anche catturare in un array e contare l'array. Stesso principio, tecnica diversa:

my @c = $y =~ /$x/g; 
my $count = @c; 
+0

Grazie! È molto simile alla seconda soluzione. – ronash

+2

@ronash È la stessa soluzione. Uno usa una variabile temporanea, l'altra no. Si potrebbe anche fare 'my $ count = @c = $ y = ~/$ x/g', ma invece si può semplicemente ignorare' @ c' e usare '()'. Qual è il migliore, se non ti importa delle partite attuali. – TLP

+2

Questo non funziona se '$ x' contiene determinati caratteri regex, poiché' $ x' viene interpretato come un'espressione regolare. Aggiungi '\ Q' per risolvere il problema, ad es. '/ \ Q $ x/G'. Vedere 'quotemeta' per ulteriori informazioni. – tuomassalo

8

È possibile utilizzare un'espressione regolare globale. Qualcosa di simile:

my @matches = $bigstring =~ /($littlestring)/g; 
my $count = @matches; 
+0

Sembra la soluzione più semplice, quindi penso che la userò, a meno che non ce ne sia una più veloce? Grazie! – ronash

+0

Non sono sicuro della regex, ma sono sicuro che usare le operazioni di matching è più veloce delle sostituzioni. E non riesco a pensare a una soluzione che non abbia qualcosa a che fare con regex (sarà molto interessante vedere il contrario!) – MattLBeck

14
my $string = "aaaabbabbba"; 
my @count = ($string =~ /a/g); 
print @count . "\n"; 

o

my $count = ($string =~ s/a/a/g); 
+0

Grazie! Funzionerà se viene richiesta più di una lettera? – ronash

+1

Ehm, sì ... è un'espressione regolare, puoi abbinare a qualsiasi cosa. –

+0

Sulla tua seconda soluzione, tr/a/a/g non sarebbe una soluzione migliore, perché sostituisci il carattere da solo e tr è più veloce su quello di s? – nerdbeere

4

Solo per completezza si può chiamare ripetutamente la funzione di indice in un ciclo e contare tutte le volte che ha restituito l'indice della stringa in la stringa e cambia la posizione di partenza. Ciò eviterebbe l'uso di espressioni regex e nei miei test è un po 'più veloce delle soluzioni regex.

Ho adattato un sub per farlo da qui: http://www.misc-perl-info.com/perl-index.html

sub occurrences { 

    my($x, $y) = @_; 

    my $pos = 0; 
    my $matches = 0; 

    while (1) { 
     $pos = index($y, $x, $pos); 
     last if($pos < 0); 
     $matches++; 
     $pos++; 
    } 

    return $matches; 
} 
Problemi correlati