2012-10-07 19 views
15

En lugar de bucle a través de cada personaje para ver si es el que usted desee y luego añadir el índice de su encendido a una lista de este modo:manera más eficiente para obtener todos los índices de un carácter en una cadena

 var foundIndexes = new List<int>(); 
    for (int i = 0; i < myStr.Length; i++) 
    { 
     if (myStr[i] == 'a') 
      foundIndexes.Add(i); 
    } 
+0

No hay manera más rápida de hacerlo para un carácter; sin embargo, si está buscando patrones más largos, existen diferentes algoritmos que le permiten omitir más de un carácter a la vez como el [algoritmo de búsqueda de cadenas de Boyer-Moore] (http://en.wikipedia.org/wiki/Boyer%) E2% 80% 93Moore_string_search_algorithm). –

Respuesta

18

Usted puede utilizar String.indexOf , ver ejemplo

string s = "abcabcabcabcabc"; 
    var foundIndexes = new List<int>(); 

    long t1 = DateTime.Now.Ticks; 
    for (int i = s.IndexOf('a'); i > -1; i = s.IndexOf('a', i + 1)) 
     { 
     // for loop end when i=-1 ('a' not found) 
       foundIndexes.Add(i); 
     } 
    long t2 = DateTime.Now.Ticks - t1; // read this value to see the run time 
+0

+1 Aunque estoy de acuerdo con el comentario de @cdhowie, es otra opción para probar si pelearé por el último bit de rendimiento. No estoy seguro de si JIted 'for' siempre eliminará los controles de límites que no están presentes en IndexOf, puede ser más rápido para cadenas largas con ocurrencias extrañas del personaje. –

+0

sus comentarios se basan en la suposición. lo he probado mi solución es mucho más rápida. pruébalo para creerlo. compare el valor de tick antes y después de las dos soluciones. He editado mi respuesta para mostrarle cómo lo pruebo –

+0

lo siento, he hecho algunas pruebas más. realmente depende de cuál sea tu combinación, puede ser más rápido y en algún momento también puede ser más lento. pero de todos modos, creo que mi código es más limpio. –

7

Cómo sobre

string xx = "The quick brown fox jumps over the lazy dog"; 

char search = 'f'; 

var result = xx.Select((b, i) => b.Equals(search) ? i : -1).Where(i => i != -1); 
+0

Casi menos eficiente algorítmicamente, solo estás usando LINQ para hacer lo mismo. A menos que haya algo mágico de LINQ que combine las dos búsquedas (no estoy íntimamente familiarizado con las optimizaciones de LINQ), en realidad ha introducido un segundo ciclo y ha empeorado el rendimiento. –

+0

@EdS .: Ningún LINQ retrasa la ejecución en él. Es realmente un solo bucle que funcionará. –

+2

Solo lazo o no, no es más rápido y simplemente convoluciona lo que debería ser una simple pieza de código. –

5

Si la cadena es corto, puede ser más eficiente para buscar la cadena de una vez y contar el número de veces que el personaje aparece, a continuación, asignar una matriz de ese tamaño y buscar la cadena por segunda vez, grabando los índices en la matriz. Esto omitirá cualquier reasignación de lista.

Lo que se reduce a cuánto tiempo es la cuerda y cuántas veces aparece el personaje. Si la cadena es larga y el personaje aparece pocas veces, buscarla una vez y agregar indicios a un List<int> será más rápido. Si el personaje aparece muchas veces, entonces buscar la cadena dos veces (una para contar y una para llenar una matriz) puede ser más rápido. Exactamente donde está el punto de inflexión depende de muchos factores que no se pueden deducir de su pregunta.

Si es necesario buscar la cadena de múltiples caracteres diferentes y obtener una lista de índices para esos personajes por separado, puede ser más rápido para buscar a través de la cadena de una vez y construir un Dictionary<char, List<int>> (o una List<List<int>> utilizando las compensaciones de carácter de \0 como las indices en la matriz externa).

En última instancia, debe comparar su aplicación para encontrar cuellos de botella. A menudo, el código que creemos funcionará lentamente es en realidad muy rápido, y pasamos la mayor parte del tiempo bloqueando la entrada/salida o la entrada del usuario.

+0

+1, estaba escribiendo algo similar, pero no tengo tiempo para ejecutar ningún punto de referencia, así que decidí no hacerlo. –

+0

howie, si no le importa que pregunte, ¿qué usaría para comparar la aplicación? No tengo VS premium, así que no puedo usar Analyze, solo uso Professional edition. –

5

La iteración bruta siempre es mejor & más optimizada.

A menos que sea una tarea compleja y poco, nunca se necesita buscar una solución mejor optimizado ...

así que yo sugeriría a continuar con:

var foundIndexes = new List<int>(); 

for (int i = 0; i < myStr.Length; i++) 

    if (myStr[i] == 'a') foundIndexes.Add(i); 
8

utilizo el siguiente método de extensión a yield todos los resultados:

public static IEnumerable<int> AllIndexesOf(this string str, string searchstring) 
{ 
    int minIndex = str.IndexOf(searchstring); 
    while (minIndex != -1) 
    { 
     yield return minIndex; 
     minIndex = str.IndexOf(searchstring, minIndex + searchstring.Length); 
    } 
} 

uso:

IEnumerable<int> result = "foobar".AllIndexesOf("o"); // [1,2] 
Cuestiones relacionadas