2012-03-02 21 views
32

¿Cuál es la manera más rápida de contar la cantidad de veces que una determinada cuerda aparece en una más grande? Mi mejor opción sería reemplazar todas las instancias de esa cadena con nada, calcular la diferencia de longitudes y dividir por la longitud de la subcadena, pero parece bastante lento, y necesito analizar grandes cantidades de datos.Contando el número de ocurrencias de una cadena dentro de otra (Perl)

+0

posible que desee comprobar esto ... aunque es a partir de 1999, y hay muy probablemente otras maneras de hacer este tipo de cosas eficientemente: 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

¿Pueden superponerse? – tchrist

Respuesta

57

Puede capturar las cadenas, y luego contarlas. Se puede hacer mediante la aplicación de un contexto de lista para la captura con ():

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

También puede capturar a una matriz y contar la matriz. El mismo principio, técnica diferente:

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

¡Gracias! Es muy parecido a la segunda solución. – ronash

+2

@ronash Es la misma solución. Uno usa una variable de temperatura, el otro no. También puede hacer 'my $ count = @c = $ y = ~/$ x/g', pero en su lugar puede simplemente ignorar' @ c' y usar '()'. Que es lo mejor, si no te importan las partidas reales. – TLP

+2

Esto no funciona si '$ x' contiene ciertos caracteres reverb, ya que' $ x' se interpreta como una expresión regular. Agrega '\ Q' para arreglar esto, ej. '/ \ Q $ x/g'. Ver 'quotemeta' para más información. – tuomassalo

8

Puede usar una expresión regular global. Algo como:

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

Parece la solución más simple, así que creo que la usaré, a menos que haya una más rápida. ¡Gracias! – ronash

+0

No estoy seguro de la velocidad de la expresión regular, pero estoy seguro de que el solo hecho de usar las operaciones de coincidencia es más rápido que las sustituciones. Y no puedo pensar en una solución que no tenga algo que ver con regex (¡será muy interesante ver lo contrario!) – MattLBeck

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

o

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

¡Gracias! ¿Funcionará eso si se busca más de una letra? – ronash

+1

Erm, sí ... es una expresión regular, puede coincidir en cualquier cosa. –

+0

En su segunda solución, ¿no sería tr/a/a/g una mejor solución, porque reemplaza el carácter por sí mismo y tr es más rápido que s? – nerdbeere

4

simplemente para la corrección puede llamar repetidamente la función de índice en un bucle y contar todas las veces que devuelve el índice de la subcadena en la cuerda, y cambie la posición de inicio. Eso evitaría el uso de expresiones regulares, y en mis pruebas es un poco más rápido que las soluciones de expresiones regulares.

He adaptado un sub hacerlo desde aquí: 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; 
} 
Cuestiones relacionadas