2012-01-24 26 views
6

devo una struttura di dati che consiste di coppie di valori, il primo dei quali è un numero intero e la seconda delle quali è una stringa alfanumerica (che può iniziare con cifre):Quale struttura dati C# consente di cercare le stringhe in modo più efficiente per le sottostringhe?

+--------+-----------------+ 
| Number | Name   | 
+--------+-----------------+ 
| 15  | APPLES   | 
| 16  | APPLE COMPUTER | 
| 17  | ORANGE   | 
| 21  | TWENTY-1  | 
| 291 | 156TH ELEMENT | 
+--------+-----------------+ 

Una tabella di questi sarebbe comprendono fino a 100.000 righe.

Vorrei fornire una funzione di ricerca in cui l'utente può cercare il numero (come se fosse una stringa) o pezzi della stringa. Idealmente la ricerca sarà "live" come l'utente digita; dopo ogni sequenza di tasti (o forse dopo un breve ritardo di ~ 250-500 ms) verrà eseguita una nuova ricerca per trovare i candidati più probabili. Così, ad esempio, la ricerca su

  • 1 tornerà 15 APPLES, 16 APPLE COMPUTER, 17 ORANGE, e 291 156TH ELEMENT
  • 15 sarà restringere la ricerca a 15 APPLES, 291 156TH ELEMENT
  • AP torneranno 15 APPLES e 16 APPLE COMPUTER
  • (idealmente , ma non richiesto) ELEM restituirà 291 156TH ELEMENT.

Stavo pensando di utilizzare due Dictionary<string, string> s poiché in definitiva le int s vengono confrontati come string s - un indice piacimento la parte intera e l'altra dalla parte stringa.

Ma la ricerca per sottostringa non dovrebbe utilizzare una funzione di hash e sembra inutile utilizzare il doppio della memoria di cui ho bisogno.

In definitiva, la domanda è: esiste un modo efficace per cercare nel testo due elenchi di grandi dimensioni contemporaneamente per sottostringhe?

In caso contrario, che ne dici di uno SortedDictionary? Potrebbe aumentare le prestazioni ma non risolverebbe il problema dell'hash.

Pensavo di creare una regex al volo, ma pensavo che avrebbe funzionato terribilmente.

Sono nuovo di C# (essendo venuto dal mondo Java), quindi non ho ancora esaminato LINQ; è questa la risposta?

MODIFICA 18:21 EST: nessuna delle stringhe nel campo "Nome" sarà più lunga di 12-15 caratteri, se ciò influisce sulla potenziale soluzione.

+0

penso un'implementazione leggermente modificata del [Algoritmo di Knuth-Morris-Pratt] (http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) sarebbe essere utile. – ChaosPandion

+0

Quando dici "efficientemente" intendi "rapidamente" o meno memoria? Generalmente in questi scenari scambi la velocità per la memoria o trovi un equilibrio accettabile tra i due. Anche la stringa 100k è abbastanza statica, il che significa che c'è poco turnover e vengono cercati ripetutamente? – EBarr

+0

@EBarr: la memoria non è una grande preoccupazione, ma non voglio sprecare. La velocità è più importante qui. – Tenner

risposta

3

Prendere in considerazione l'utilizzo della struttura dati Trie.

Come raggiungerlo? Le foglie rappresenterebbero la tua "riga", ma avresti "due percorsi" per ogni istanza di memoria di una "riga" (una per il numero e l'altra per il nome).

È quindi possibile sacrificare la sua condizione:

(ideally, but not required) ELEM will return 291 156TH ELEMENT. 

O fornire ancora più percorsi alle istanze di fila.

+0

Interessante; Cercherò sicuramente di implementarlo e vedere come si comporta. Non ho incluso questo fatto nel post originale, ma probabilmente posso fare la creazione iniziale dell'albero all'avvio del programma; se ciò richiede un po 'di tempo in più non è certamente la fine del mondo. Grazie! – Tenner

+0

Macchia qui. Picchiami al punzone ;-) – EBarr

+0

È più una soluzione "cattiva" che "una ottima in termini di utilizzo della memoria". È quello che ti fa piangere come un bambino quando lo implementa :) Come accennato da Phil, Lucene.Net è una buona soluzione, ma in realtà dipende dal tuo caso d'uso specifico. 100k di tali stringhe ... probabilmente ~ 1 MB. Non molto se li hai disponibili in memoria, ma dovresti estrarli dal database molte volte su richiesta e creare prima un trie, poi questa è un'altra storia. – doblak

6

Se possibile, evitare di caricare tutte le 100.000 voci in memoria. Vorrei utilizzare un database o Lucene.Net per indicizzare i valori. Quindi utilizzare la sintassi di query appropriata per cercare in modo efficiente i risultati.

+2

Che prende tutto il divertimento fuori però .... – ChaosPandion

+0

Quello che ho delineato sopra è una parte molto piccola del prodotto, e io preferirei davvero la soluzione più leggera possibile. Detto questo, considererò certamente Lucene.net in memoria se non riesco a trovare qualcos'altro che funzioni bene. Grazie! – Tenner

1

Poiché si sta cercando l'inizio di parole, le raccolte basate su chiavi non funzioneranno, a meno che non si memorizzino tutti i pezzi possibili delle parole, come "a", "ap", "app", "appl", "mela ".

Il mio suggerimento è quello di utilizzare un System.Collections.Generic.List<T> in congiunzione con una ricerca binaria. Dovresti fornire il tuo IComparer<T>, che trova anche l'inizio delle parole. Dovresti usare due strutture dati.

Uno List<KeyValuePair<string,int>> contenente singole parole o il numero come chiave e il numero come valore.

Uno Dictionary<int,string> con il nome intero.

Si potrebbe procedere in questo modo:

  1. dividere la frase (il nome completo) in singole parole.

  2. Aggiungili all'elenco con la parola chiave e il numero come valore di KeyValuePair.

  3. Aggiungere il numero all'elenco come chiave e valore dello KeyValuePair.

  4. Quando l'elenco è pieno, ordinare l'elenco per consentire una ricerca binaria.

Ricerca di un inizio di una parola:

  1. Cerca nella lista utilizzando BinarySearch in congiunzione con il IComparer<T>.

  2. L'indice che si ottiene dalla ricerca potrebbe non essere il primo che si applica, quindi tornare all'elenco finché non si trova la prima voce corrispondente.

  3. Utilizzando il numero memorizzato come valore nell'elenco, cercare l'intero nome nel dizionario utilizzando questo numero come chiave.

Problemi correlati