Quale algoritmo di ricerca delle stringhe è in realtà il più veloce?


27

Sono rimasto bloccato per qualche tempo su quale sia l'algoritmo di ricerca delle stringhe più veloce, ho sentito molte opinioni, ma alla fine non ne sono sicuro.

Ho sentito alcune persone dire che l'algoritmo più veloce è Boyer-Moore e alcuni che affermano che Knuth-Morris-Pratt è in realtà più veloce.

Ho cercato la complessità di entrambi, ma per lo più sembrano uguali O(n+m). Ho scoperto che nel peggiore dei casi Boyer-Moore ha una O(nm)complessità rispetto a Knuth-Morris-Pratt che ha O (m + 2 * n). Dove n = lunghezza del testo e m = lunghezza del motivo.

Per quanto ne so Boyer-Moore ha un caso peggiore lineare se usassi la Regola Galil.

La mia domanda, nel complesso l'algoritmo di ricerca di stringhe più veloce (questa domanda include tutti i possibili algoritmi di puntura, non solo Boyer-Moore e Knuth-Morris-Pratt).

Modifica: grazie a questa risposta

Quello che sto cercando esattamente è:

Dato un testo Te uno schema Pdevo trovare tutte le apparenze di Pin T.

Anche la lunghezza di P e T proviene da [1,2 000 000]e il programma deve funzionare sotto 0,15 sec.

So che KMP e Rabin-Karp sono sufficienti per ottenere un punteggio del 100% sul problema, ma io per primo volevo provare a implementare Boyer-Moore. Quale sarebbe meglio per questo tipo di ricerca di pattern?


6
Quando li hai testati nella tua lingua preferita, cosa hai trovato?
Walter,

4
In alcuni test Boyer-Moore era migliore su altri KMP era meglio, ma non sono sicuro di averne la "migliore" implementazione. Per quanto riguarda la lingua scelta, è nei tag: C ++ (non sono sicuro se l'hai visto da quando hai scritto "lingua scelta"). PS Non sono nemmeno sicuro di aver testato i migliori test.
Vandamon Taigi,


Knuth-Morris-Pratt che ha O (m + 2 * n) ... Intendi O (m + n).
Jules,

Scegline uno con una discreta complessità algoritmica e poi metti a punto il caos con un profiler in mano - ha sempre funzionato per me. :-D

Risposte:


38

Dipende dal tipo di ricerca che si desidera eseguire. Ciascuno degli algoritmi funziona particolarmente bene per alcuni tipi di ricerca, ma non hai indicato il contesto delle tue ricerche.

Ecco alcuni pensieri tipici sui tipi di ricerca:

  • Boyer-Moore: lavora pre-analizzando il modello e confrontandolo da destra a sinistra. Se si verifica una mancata corrispondenza, l'analisi iniziale viene utilizzata per determinare fino a che punto il modello può essere spostato rispetto al testo cercato. Questo funziona particolarmente bene per lunghi schemi di ricerca. In particolare, può essere sublineare, poiché non è necessario leggere ogni singolo carattere del testo.

  • Knuth-Morris-Pratt: pre-analizza anche il modello, ma cerca di riutilizzare tutto ciò che era già abbinato nella parte iniziale del modello per evitare di doverlo ripetere. Questo può funzionare abbastanza bene, se il tuo alfabeto è piccolo (es. Basi di DNA), poiché hai una maggiore probabilità che i tuoi schemi di ricerca contengano sottotitoli riutilizzabili.

  • Aho-Corasick: ha bisogno di molta preelaborazione, ma lo fa per un certo numero di modelli. Se sai che cercherai gli stessi schemi di ricerca più e più volte, allora questo è molto meglio dell'altro, perché devi analizzare gli schemi solo una volta, non una volta per ricerca.

Quindi, come al solito in CS, non esiste una risposta definitiva al meglio in generale . Si tratta piuttosto di scegliere lo strumento giusto per il lavoro da svolgere.

Un'altra nota sul tuo ragionamento nel caso peggiore: considera i tipi di ricerche necessarie per creare quel caso peggiore e pensa a fondo se questi sono davvero rilevanti nel tuo caso. Ad esempio, la O(mn)complessità del caso peggiore dell'algoritmo di Boyer-Moore deriva da un modello di ricerca e da un testo che utilizzano ciascuno solo un carattere (come la ricerca aaain aaaaaaaaaaaaaaaaaaaaa) - hai davvero bisogno di essere veloce per ricerche del genere?


Ho quasi tutto l'alfabeto inglese da usare e ho aggiornato la domanda, mi spiace di non aver iniziato con questo durante l'accattonaggio.
Vandamon Taigi,

E sì, devo essere veloce anche per ricerche del genere
Vandamon Taigi,

1

Anche se sono leggermente in ritardo per rispondere a questa domanda, ma penso che Z-Algorithmsia molto più veloce di qualsiasi altra sua controparte. La sua complessità nel caso peggiore è O (m + n) e non richiede la preelaborazione del modello / testo. È anche molto facile codificare rispetto agli altri algoritmi.

Funziona nel modo seguente.

Ad esempio, c'è una stringa S ='abaaba'. Dobbiamo trovare z(i)valori per i=0 to len(S)-1. Prima di entrare nella spiegazione, lasciatemi stabilire alcune definizioni.

z(i)= no. di caratteri del prefisso Sche corrisponde al prefisso di s(i).

s(i)= ithsuffisso di S.

I seguenti sono i s(i)valori per s = 'abaaba'.

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

I valori z sono rispettivamente

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

Per una comprensione dettagliata dell'algoritmo, consultare i seguenti collegamenti.

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

Ora ci vuole O (N) per trovare tutti i zvalori senza alcun sovraccarico di pre-elaborazione. Ci si potrebbe chiedere ora come è possibile utilizzare questa logica per abbinare il modello in una determinata stringa?

Vediamo con un esempio. Modello (P): aba, Testo (T): aacbabcabaad.

Metti questo nel modulo P $ T. ( $- qualsiasi carattere che non appare in uno schema o testo. Prenderò in considerazione l'importanza di $tra poco.)

P$T = aba$aacbabcabaad

Sappiamo len(P)= 3.

Tutti i valori z di P$Tsono

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

Ora che z(i)= len(P). Ans = 11.Quindi il nostro modello è presente in Ans-len(P)-1= 7. -1è per il $personaggio.

Ora perché $o qualsiasi personaggio così speciale è importante. Considerare P = 'aaa'e T = 'aaaaaaa'. Senza il carattere speciale, tutti z(i)avranno valori incrementali. Si può ancora trovare la posizione del modello nel testo con le seguenti formule:

Condizione: z(i)> = len(P)e posizione: Ans-len(P). Ma la condizione in questo caso diventa un po 'complicata e confusa. Personalmente preferisco usare la speciale tecnica del personaggio.


1
Potresti spiegarlo qui? Avere collegamenti a siti esterni può essere utilizzato per elaborare, ma il nucleo di una risposta dovrebbe essere nella risposta stessa piuttosto che dover seguire un collegamento a un altro sito.

L'algoritmo z è sostanzialmente lo stesso di kmp. Dubito che sia molto più veloce.
Thomas Ahle,

2
Sono d'accordo con @ThomasAhle. Il calcolo z è preelaborazione. È una buona spiegazione, comunque. Ho trovato un O(n)modo per passare dalla pre-elaborazione KMP alla pre-elaborazione Z, grazie a questa risposta. Qui
leewz,

-1

Utilizzare la memoria indirizzabile del contenuto , implementata nel software sotto forma di indirizzamento virtuale (indicando le lettere in lettere).

È un po 'superfluo per un algoritmo di adattamento delle stringhe medio.

CAM può abbinare un numero enorme di modelli contemporaneamente, fino a circa 128 lettere (se sono ASCII; se sono Unicode solo 64). Ed è una chiamata per lunghezza di lettera nella stringa che si desidera abbinare e una lettura casuale dalla memoria per lunghezza della lunghezza massima del modello. Quindi, se stavi analizzando una stringa di 100.000 lettere, con fino a 90.000.000 di pattern contemporaneamente (che richiederebbero circa 128 GiB per memorizzare un numero di pattern così grande), occorrerebbero 12.800.000 letture casuali dalla RAM, quindi accadrebbe in 1ms.

Ecco come funziona l'indirizzamento virtuale.

Se comincio con 256 indirizzi di partenza, che rappresentano la prima lettera, queste lettere indicano 256 delle lettere successive. Se un modello è inesistente, non lo memorizzi.

Quindi, se continuo a collegare le lettere alle lettere, è come avere 128 porzioni di indirizzi virtuali che puntano a indirizzi virtuali.

Funzionerà - ma per arrivare a 900.000.000 di modelli contemporaneamente corrispondenti, c'è un ultimo trucco da aggiungere ad esso - e sta sfruttando il fatto che inizi con un sacco di riutilizzo di questi buffer di lettere, ma in seguito si disperde. Se elenchi i contenuti, invece di allocare tutti i 256 caratteri, allora rallenta molto poco e otterrai un aumento della capacità di 100 volte, perché alla fine ottieni solo 1 lettera utilizzata in ogni buffer di puntatori di lettere (che ho soprannominato " fuga ').

Se vuoi ottenere una corrispondenza della stringa più vicina, allora molte di queste sono in esecuzione in parallelo e le raccogli in una gerarchia, quindi diffondi l'errore in modo imparziale. se provi al vicino più vicino con solo uno, allora sei di parte verso l'inizio dell'albero.


4
@MagnusRobertCarlWoot dato che hai lo stesso gavatar di roucer81, è una coincidenza astronomica di collisione del codice hash o hai lo stesso indirizzo email. Se sei lo stesso individuo dietro entrambi gli account, dovresti utilizzare il modulo "contattaci" per unirli in modo da ottenere il giusto credito per la reputazione acquisita attraverso i voti su questa risposta.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.