2012-10-07 11 views
15

Invece di loop attraverso ogni personaggio per vedere se è quello che si desidera quindi aggiungendo l'indice vostra su una lista in questo modo:modo più efficiente per ottenere tutti gli indici di un carattere in una stringa

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

Non c'è modo più veloce per farlo per un carattere; tuttavia, se stai cercando pattern più lunghi, esistono diversi algoritmi che ti permettono di saltare più di un personaggio alla volta come l'algoritmo [Boyer-Moore string search] (http://en.wikipedia.org/wiki/Boyer% E2% 80% 93Moore_string_search_algorithm). –

risposta

18

È possibile utilizzare String.indexOf , vedi esempio

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 Pur essendo d'accordo con il commento di @cdhowie, è un'altra opzione da provare se si combatte per l'ultimo bit di prestazioni. Non sono sicuro che se JIted 'for' rimuoverà sempre i controlli ai confini che non sono presenti in IndexOf, potrebbe essere più veloce per la stringa lunga con rare occorrenze del personaggio. –

+0

i tuoi commenti sono basati sull'assunzione. ho provato la mia soluzione è molto più veloce. prova a crederci confrontare il valore di tick prima e dopo delle due soluzioni. ho modificato la mia risposta per mostrarti come lo collaudo –

+0

scusa, ho fatto qualche altro test. in realtà dipende da quale è la tua combinazione, può essere più veloce e qualche volta può anche essere più lento. ma comunque, penso che il mio codice sia più pulito. –

7

Come circa

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

Algoritmo difficilmente più efficiente, si sta semplicemente utilizzando LINQ per fare la stessa cosa. A meno che non ci sia qualche magia LINQ che combina le due ricerche (non ho familiarità con le ottimizzazioni LINQ), hai effettivamente introdotto un secondo ciclo e peggiorato le prestazioni. –

+0

@EdS: No LINQ esegue l'esecuzione in ritardo. In realtà è un singolo ciclo che funzionerà. –

+2

Singolo ciclo o no, non è più veloce e convoluta solo quello che dovrebbe essere un semplice pezzo di codice. –

5

Se la stringa è breve, può essere più efficiente per cercare la stringa volta e contare il numero di volte che il carattere viene visualizzato, quindi allocare un array di tali dimensioni e cerca la stringa una seconda volta, registrare gli indici nella matrice. Questo salterà qualsiasi riassegnazione di liste.

Quello che viene fuori è la durata della stringa e il numero di volte in cui il carattere appare. Se la stringa è lunga e il carattere appare poche volte, cercarlo una volta e aggiungere gli indici a List<int> sarà più veloce. Se il personaggio appare molte volte, cercare la stringa due volte (una volta per contare e una volta per riempire un array) potrebbe essere più veloce. Esattamente dove il punto di svolta dipende da molti fattori che non possono essere dedotti dalla tua domanda.

Se avete bisogno di cercare la stringa di caratteri multipli diversi e ottenere un elenco di indici per quei personaggi a parte, può essere più veloce per la ricerca in stringa una volta e costruire un Dictionary<char, List<int>> (o un List<List<int>> utilizzando scostamenti di caratteri da \0 come gli indizi nell'array esterno).

In definitiva, è necessario eseguire il benchmark dell'applicazione per individuare i colli di bottiglia. Spesso il codice che pensiamo si esibirà lentamente è in realtà molto veloce, e passiamo la maggior parte del nostro tempo a bloccare l'I/O o l'input dell'utente.

+0

+1, stavo scrivendo qualcosa di simile, ma non ho il tempo di eseguire alcun benchmark, così ho deciso di non farlo. –

+0

howie, se non ti dispiace chiederti, cosa utilizzeresti per confrontare l'app? Non ho VS premium, quindi non posso usare Analyze, uso solo la versione Professional. –

5

L'iterazione raw è sempre migliore & ottimizzata.

A meno che non si tratta di un compito complesso e po ', non hai mai veramente bisogno di cercare una soluzione migliore ottimizzata ...

quindi vorrei suggerire di continuare con:

var foundIndexes = new List<int>(); 

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

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

uso il seguente metodo di estensione a yield tutti i risultati:

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

utilizzo:

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