Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Algoritmi e Strutture Dati Algoritmi di String Matching Maria Rita Di Berardini, Emanuela Merelli1 1 Dipartimento di Matematica e Informatica Università di Camerino A.A. 2007/08 Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Il problema dello string matching Trovare tutte le occorrenze di una data stringa (pattern) all’interno di un testo Applicazioni: programmi di elaborazione dei testi, cercare particolari sequenze di DNA Il testo viene rappresentato mediante un array T [1..n] di lunghezza n Il pattern viene rappresentato con un array P[1..m] di lunghezza m Sia il testo che il pattern sono stringhe su un alfabeto finito Σ (ad esempio: Σ = {0, 1} oppure Σ = {a, b, ..., z}) Diciamo che P occorre con uno spostamento s nel testo T se 0 ≤ s ≤ n − m e T [s + 1 .. s + m] = P[1 .. m] (ossia se T [s + j] = P[j] per 1 ≤ j ≤ m). In questo caso diciamo che s è uno spostamento valido per P Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt L’algoritmo “naive” di string matching Trova tutti gli spostamenti validi utilizzando un ciclo che verifica la condizione T [s + 1 .. s + m] = P[1 .. m] per ciascun degli n − m + 1 valori possibili di s 1 2 3 4 5 Naive-String-Matcher(T , P) n ← length[T ] m ← length[P] for s ← 0 to n − m do if T [s + 1 .. s + m] = P[1 .. m] then stampa “pattern con spostamento s” Richiede un tempo O((n − m + 1)m) (questo limite è stretto nel caso peggiore) Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Algoritmo di Rabin-Karp Usa una funzione hash per associare un valore numerico al pattern e ad ogni sottostringa del testo lunga m Indichiamo con p il valore numerico del pattern e con ts il valore di T [s + 1 .. s + m], per ogni s = 0, . . . , n − m Se p 6= ts , lo spostamento non può essere valido e calcoliamo il valore della prossima sottostringa Se p = ts , possiamo confrontare T [s + 1, .. s + m] con P[1, .. m] (esattamente come l’algoritmo naive) Questo consente di ridurre drasticamente il numero di confronti tra pattern e sottostringhe del testo Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash Idea: considerare una sequenza di m caratteri come un numero di m cifre in base b (dove b tipicamente è la dimensione dell’alfabeto Σ) La sottostringa T [s + 1, .. s + m] diventa il numero ts = T [s + 1] · b m−1 + T [s + 2] · b m−2 + . . . + T [s + m − 1]b + T [s + m] Inoltre, possiamo ottenere ts+1 da ts come segue 1 Moltiplichiamo ts per b (shift a sinistra di una cifra) ottenendo T [s + 1] · b m + T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash Idea: considerare una sequenza di m caratteri come un numero di m cifre in base b (dove b tipicamente è la dimensione dell’alfabeto Σ) La sottostringa T [s + 1, .. s + m] diventa il numero ts = T [s + 1] · b m−1 + T [s + 2] · b m−2 + . . . + T [s + m − 1]b + T [s + m] Inoltre, possiamo ottenere ts+1 da ts come segue 1 Moltiplichiamo ts per b (shift a sinistra di una cifra) ottenendo T [s + 1] · b m + T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b 2 Sotttraimo il valore T [s + 1] · b m (sottrai la cifra più a sinistra) ottenendo T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash Idea: considerare una sequenza di m caratteri come un numero di m cifre in base b (dove b tipicamente è la dimensione dell’alfabeto Σ) La sottostringa T [s + 1, .. s + m] diventa il numero ts = T [s + 1] · b m−1 + T [s + 2] · b m−2 + . . . + T [s + m − 1]b + T [s + m] Inoltre, possiamo ottenere ts+1 da ts come segue 1 Moltiplichiamo ts per b (shift a sinistra di una cifra) ottenendo T [s + 1] · b m + T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b 2 Sotttraimo il valore T [s + 1] · b m (sottrai la cifra più a sinistra) ottenendo T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b 3 Sommiano il valore T [s + m + 1] (aggiungi una nuova cifra a destra) ottenendo T [s +2]·b m−1 +. . .+T [s +m−1]·b 2 +T [s +m]·b+T [s +m+1] = ts+1 Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash Idea: considerare una sequenza di m caratteri come un numero di m cifre in base b (dove b tipicamente è la dimensione dell’alfabeto Σ) La sottostringa T [s + 1, .. s + m] diventa il numero ts = T [s + 1] · b m−1 + T [s + 2] · b m−2 + . . . + T [s + m − 1]b + T [s + m] Inoltre, possiamo ottenere ts+1 da ts come segue 1 Moltiplichiamo ts per b (shift a sinistra di una cifra) ottenendo T [s + 1] · b m + T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b 2 Sotttraimo il valore T [s + 1] · b m (sottrai la cifra più a sinistra) ottenendo T [s + 2] · b m−1 + . . . + T [s + m − 1] · b 2 + T [s + m] · b 3 Sommiano il valore T [s + m + 1] (aggiungi una nuova cifra a destra) ottenendo T [s +2]·b m−1 +. . .+T [s +m−1]·b 2 +T [s +m]·b+T [s +m+1] = ts+1 Ricapitolando ts+1 = (ts · b − T [s + 1] · b m ) + T [s + m + 1] Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash – Un esempio Σ = {0, 1, . . . , 9}, T = 12345678 e P = 345 (m = 3) Sia il pattern che le sottostringhe vengono considerate come numeri in base 10 p = 345, t0 = 123, t1 = 234, t2 = 345, t3 = 456, t4 = 567 e t5 = 678 ts+1 = (ts · b − T [s + 1] · b m ) + T [s + m + 1] t1 = (t0 ·10−T [1]·103 )+T [4] = (123·10−1000)+4 = 230+4 = 234 t2 = (t1 ·10−T [2]·103 )+T [5] = (234·10−2000)+5 = 340+5 = 345 ... L’algoritmo verifica che p 6= t0 , p 6= t1 , ma p = t2 . A questo punto verifica che P = 345 = T [2 + 1 .. 2 + m] = 345 e restituisce lo spostamento 2 Poichè p 6= t3 , t4 , t5 non produce altri risultati Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Funzione Hash mod L’algoritmo di Rabin-Karp esegue una serie di confronti e operazioni aritmetiche (si veda il calcolo di ts+1 a partire da ts ) su numeri di m cifre Se si vuole che l’algoritmo sia efficiente, è necessario poter eseguire queste operazioni in un tempo costante Se m è molto grande, i valori di p e dei vari ts potrebbero essere troppo grandi per lavorarci in maniera conveniente Per questo motivo è utile calcolare i valori p e ts con 0 ≤ s ≤ n − m modulo un qualche numero primo q Aumenta la possibilità che si verifichino i cosidetti falsi successi, i.e. i casi in cui p = ts ma T [s + 1 .. s + m] 6= P[1 .. m] Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Alcuni dettagli dell’algoritmo Come calcoliamo i valori associati al patter P e a T [1 .. m], ossia p e t0 (che verra poi usato per calcolare gli altri ts ) modulo un dato q? Negli esempi che seguono assumiamo q = 11 e la base b = 10 Consideriamo P = 235, il suo valore ossia p = 235 mod 11 = 4. Possiamo calcolare p mediante la seguente sequenza di valori: p0 = 0 p1 = (b · p0 + P[1]) mod 11 = 2 mod 11 = 2 p2 = (b · p1 + P[2]) mod 11 = (10 · 2 + 3) mod 11 = 1 p3 = (b · p2 + P[3]) mod 11 = (10 · 1 + 5) mod 11 = 4 I valori p e t0 vengono calcolati mediante il seguente frammento di codice p ← 0, t0 ← 0 for i ← 1 to m do p ← (b · p + P[i]) t0 ← (b · t0 + T [i]) Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Alcuni dettagli dell’algoritmo Anche i successi valori valori di ts devono essere calcolati modulo q Ossia per 1 ≤ s ≤ n − m, ts+1 = (ts · b − T [s + 1] · b m ) + T [s + m + 1] mod q = b(ts − T [s + 1] · b m−1 ) + T [s + m + 1] mod q = b(ts − T [s + 1] · h) + T [s + m + 1] mod q dove h = b m−1 Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt L’algoritmo di Rabin-Karp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Rabin-Karp-Matcher(T , P, b, q) n ← length[T ] m ← length[P] h ← b m−1 p←0 t0 ← 0 for i ← 1 to m do p ← (b · p + P[i]) t0 ← (b · t0 + T [i]) for s ← 0 to n − m do if p = ts then if P[1 .. m] = T [s + 1 .. s + m] then stampa “pattern con spostamento s” if s < n − m then ts+1 = b(ts − T [s + 1] · h) + T [s + m + 1] mod q Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Calcolo della complessità Il calcolo di h (riga 3) può essere effettuato in O(m) Il calcolo di p e t0 viene effettuato in O(m) (ciclo for di righe 6-8) Il ciclo for di righe 9-14 esegue n − m + 1 confronti del tipo p = ts (ognuno in un tempo costante) più il numero di confronti (al più m) per stabilire se P[1 .. m] = T [s + 1 .. s + m] (a) nel caso peggiore (ossia quando tutti gli spostamenti sono validi) il test (a) viene eseguito n − m + 1 volte ed ogni volta richiede m confronti. In totale abbiamo m(n − m + 1) confronti Quindi, nel caso peggiore il for di righe 9-14, e quindi l’algoritmo, ha un costo dell’ordine di O((m + 1)(n − m + 1)) = O(m(n − m + 1)) Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Calcolo della complessità In molte applicazioni sono previste solo pochi spostamenti validi, talvolta soltanto un numero costante c In questi casi, il test (a) viene eseguito solo c volte e richiede al più cm confronti Il ciclo for di righe 9-14 esegue al più n − m + 1 + cm = n + (c − 1)m + 1 confronti L’algoritmo, ha un costo dell’ordine di O(n + (c − 1)m + 1)) = O(n + m) Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt Algoritmo di Knuth-Morris-Pratt (KMP) s+1 s+q b a c b a b a b a a b c b a b s a b a b a c a q k s’ a b a b a c a s’+1 Di Berardini, Merelli s’+k Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt La funzione fallimento/prefisso Sapendo che i primi q caratteri del pattern P[1 .. q] coincidono con i caratteri del testo T [s + 1 .. s + q] trovare il minimo spostamento s 0 tale che P[1 .. k] = T [s 0 + 1 .. s 0 + k] con s 0 + k = s + q (e quindi s 0 = s + (q − k)) Che relazione esiste tra Pq = P[1 .. q] e Pk = P[1 .. k]? Sappiamo che P[1 .. k] = T [s 0 + 1 .. s 0 + k] con s 0 + 1 > s + 1 e s0 + k = s + q Pk è un suffisso proprio di T [s + 1 .. s + q] = Pq Siano x, y ∈ Σ∗ . Diciamo che y è un suffisso di x se esiste una stringa w ∈ Σ∗ tale che x = wy . Quando y 6= allora diciamo che y è un suffisso proprio di x (denotato con y = x) Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt La funzione fallimento/prefisso Stiamo cercando un Pk (prefisso del pattern P) che sia anche un suffisso di Pq ma k deve anche minimizzare il valore di s 0 = s + (q − k) Quindi k è il più grande k < q tale che Pk = Pq Definition (la funzione fallimento/prefisso) π[q] = max{k : k < q e Pk = Pq } Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt L’algoritmo di KMP 1 2 3 4 5 6 7 8 9 10 11 12 13 KMPMatcher(T , P) π ← KMPPrefixFunction(P) i ←1 j ←1 while i ≤ n do if P[j] = T [i] then if j = m .match trovato then stampa “pattern con spostamento i − m + 1 i ←i +1 j ←j +1 else if j > 1 then j ← π[j − 1] + 1 else i ←i +1 Di Berardini, Merelli Algoritmi e Strutture Dati Definizione del problema Algoritmo di Rabin-Karp Algoritmo di Knuth-Morris-Pratt La funzione KMPPrefixFunction 1 3 4 5 6 7 8 10 11 12 13 14 KMPPrefixFunction(P) i ←2 j ←1 while i ≤ m do if P[j] = P[i] then π[i] ← j i ←i +1 j ←j +1 else if j > 1 then j ← π[j − 1] + 1 else π[i] ← 0 i ←i +1 Di Berardini, Merelli Algoritmi e Strutture Dati