String Matching Análisis y diseño de algoritmos II String Matching Encontrar todas las ocurrencias de un patrón en un texto. Texto Patrón a b c a b a a a b a a b c a b Aplicaciones Funcionalidad de editores de texto Patrones en secuencias de ADN a c String Matching El texto y el patrón están definidos sobre un mismo alfabeto Σ. Texto a b c a b a a Patrón a b a a b c a b a c El texto es un arreglo de n elementos:T[1..n] El patrón es un arreglo de m elementos (m≤n): P[1..m] Ocurrencia del patrón con desplazamiento s: (∃s: 0≤s≤n-m:(∀j:1≤j≤m:T[s+j]=P[j])) String Matching Notación “w es prefijo de x” (w ⊏ x) si x= w.y (yεΣ*). Si w ⊏ x luego, │w│ ≤ │ x │ Ejemplo ab ⊏ abcac “w es sufijo de x” (w ⊐ x) si x = y.w Ejemplo cab ⊐ abcab La notación Pk es equivalente a P[1..k], representa a los k caracteres prefijos de P. Tk representa a los k caracteres prefijo de T. String Matching Notation x,y,z: cadenas/ x ⊐ z y y⊐z. Si |x|≤ |y|, luego x⊐y. Si |x| ≥|y|, luego y⊐x. Si |x|=|y| luego x=y X X X Z Z Z Y Y Y X X X Y Y Y String Matching Un algoritmo “ingenuo” Encontrar todos los desplazamientos s en el rango 0≤s≤n-m / P⊐Ts+m P ocurre sólo una vez en T, en el desplazamiento s = 2 String Matching Un algoritmo “ingenuo” STRING-MATCHER (T:Texto; P:Patrón) {n = long(T); m= long(P); for (s=0; s <= n-m; s++) if ( P[1..m] == T[s+1..s+m]) cout << “Matching con desplazamiento”<<s; } String Matching- Algoritmos clásicos Tiempo Tiempo Preprocesamiento Matching Rabin-Karp O(m) O((n - m + 1)m) Autómata finito O(m |Σ|) O(n) Knuth-Morris-Pratt O(m) O(n) Boyer-Moore O((n-m+1)m+|Σ| O(m) String Matching Algoritmo de Rabin-Karp Se basa en nociones elementales de la teoría de números, tal como la equivalencia de dos números módulo un tercero. Sugerencia: Consultar los apuntes de Matemática Discreta o Cormen, capítulo 31 String Matching Algoritmo de Rabin-Karp Supongamos que el alfabeto es Σ={0,1,2,3,4,5,6,7,8,9} Una cadena de k caracteres consecutivos representa un número decimal de longitud k. Por ejemplo la cadena 31415. En el caso general, asumimos que cada caracter es un dígito en un sistema d-ario, donde d=|∑. String Matching Algoritmo de Rabin-Karp Sean P[1..M] y T[1..n] el patrón y texto. p es el valor decimal de P ts es el valor decimal de T[s+1..s+m] (0≤s≤ n-m) ts = p si y sólo sí T[s+1..s+m]=P[1..m] String Matching Algoritmo de Rabin-Karp Cálculo de p en O(m) usando la regla de Horner p = P[m]+10(P[m-1]+10(P[m-2]+ …10(P[2]+ 10 (P[2] + 10P[1])…)) El valor de t0 puede ser calculado desde T[1..m] en O(m). Para calcular t1, t2,…,tn-m en tiempo O(n-m) es suficiente observar que ts+1 puede ser calculado desde ts en un tiempo constante: ts+1= 10(ts -10m-1T[s+1]) + T[s+m+1] String Matching Algoritmo de Rabin-Karp ts+1= 10(ts -10m-1T[s+1]) + T[s+m+1] Si m=5 y ts=31415 ts+1=10 (31415 -10000.3) +2 = 14152 String Matching Algoritmo de Rabin-Karp ts+1= 10(ts -10m-1T[s+1]) + T[s+m+1] Si 10m-1 es precalculada, el cálculo de ts+1 es constante. Luego, p, t0, t1,..tn-m pueden ser calculados en O(n+m). Es decir, todas las ocurrencias de P[1..m] en el texto T[1..n] pueden ser calculadas en O(n+m). Sin embargo, si p y ts son muy grandes no se puede considerar que las operaciones aritméticas sobre p se calculan en tiempo constante. Luego, Rabin y Karp proponen trabajar con módulos de p y ts. String Matching Algoritmo de Rabin-Karp Supongamos que el patrón sea 31415, otras cadenas como 67399 comparten valores.Trabajar con módulos permite descartar ocurrencias pero no garantiza la ocurrencia del patrón. mod 13 válido inválido String Matching Algoritmo de Rabin-Karp 14152 ≡ (31415 -3. 10000).10 + 2 (mod 13) 14152 ≡ (7 – 3.3 ) 10 + 2 (mod 13) 8 7 ≡ 8 mod 13 Recordar! x mod y ≡ x – y x/y -18 mod 13 ≡ -18 -13 -18/13 -18 mod 13 ≡ 8 mod 13 ...-18, -5, 8, 21,... son todos “congruentes módulo 13” String Matching Algoritmo de Rabin-Karp Ts+1= (d (ts –T[s+1] h) + T[s+m+1]) mod q h= dm-1 (mod q) Sugerencia: Para un alfabeto d-ario elegir un q / d.q pueda almacenarse en una palabra de computadora String Matching Algoritmo de Rabin-Karp Rabin-Karp-Matcher (T, P, d,q) {n=long(T); m= long(P); h= dm-1 (mod q); p = 0; to = 0; for (i=1; i<=m; i++) { p = (d. p + P[i]) (mod q); to = d. t0 + T[i]) (mod q);} for (s=0; s <=n-m; s++) {if (p == ts) if (P[1..m] == T[s+1..s+m] ) cout<< “matching con desplazamiento”<< s; else if (s < n-m) ts+1= (d. (ts –T[s+1].h) + T[s+m+1]) mod q }} O((n-m+1)m) String Matching “Fuerza bruta” vs Rabin-Karp Tiempo Tiempo Preprocesamiento Matching “Alg. ingenuo” O(1) O((n-m+1) m) O(m) O((n - m + 1)m) Rabin-Karp Sugerencias Analizar el comportamiento promedio de Rabin-Karp vs. fuerza bruta Analizar si es posible adaptar Rabin-Karp para reconocer varios patrones en un mismo proceso. String Matching basado en Autómata Finito (AF) Para cada patrón P existe un AF. Este autómata es construido en la etapa de preprocesamiento. Si P= ababaca se construiría el siguiente AF: a a a a a b 0 1 a 2 a b 3 c 4 5 b a b a 6 7 String Matching basado en AF Notación AF Un autómata finito M se define mediante la tupla <Q,q0,A, ∑, δ > donde Q es un conjunto finito de estados, q0∈Q, es el estado inicial A⊆Q, conjunto de estados finales ∑, alfabeto de entrada δ :Q x ∑->Q es la función de transición φ(ε) = q0 φ(wa)= δ(φ(w),a) (transiciones sobre cadenas) String Matching basado en AF a a a a b 0 1 a 2 a b 3 4 5 b a b c a 6 7 String Matching basado en AF Definición Función sufijo correspondiente a un patrón P[1..m] σ : ∑* ->{0,1,…,m}/ σ(x) es la longitud del prefijo más largo de P que es sufijo de x: P σ(x) ≡ {k / Pk ⊐ x} σ(Y)= |Y| X Y String Matching basado en AF Función sufijo Si P = ab σ(ε) = 0 σ(ccaca) = 1 σ(ccaab) = 2 P0 = ε es sufijo de cualquier x Dado un patrón P de longitud m σ(x) = m si y sólo sí P ⊐ x. Si x ⊐y, luego σ(x) ≤ σ(y) String Matching basado en AF Definición del AF para P[1..m] Q es {0,1,…,m} q0 es 0 A ={m} δ(q,a) = σ(Pqa) El AF mantiene invariante las transiciones φ(Ti) = σ(Ti) String Matching basado en AF FINITE-AUTOMATON-MATCHER(T, δ,m) {n = long(T); q = 0; for (i=1; i<=n;i++) {q = δ(q, T[i]); if (q == m) {s = i-m; cout<<“Matching con desplazamiento”<< s; }} String Matching basado en AF COMPUTE-TRANSITION-FUNCTION(P,Σ) 1 {m = long (P); 2 for (q=0;q<=m;q++) 3 for cada caracter a∈∑ 4 {k = min (m+1, q+2); 5 repeat 6 k=k-1 7 until Pk ⊐ Pqa 8 δ(q,a) = k; } } String Matching basado en AF Ejemplo P = ababaaa m=7 (1) q=0 (2) Símbolo a k=min(8,2)=2 (4) k=1 (5) P1 ⊐ P0a m=7 (1) q=0 (2) símbolo b k=min(8,2)=2 (4) k=1 (5) P1 ⊐ P0b δ(0,a)=1 δ(0,b)=0 String Matching basado en AF a a a a b 0 1 a 2 a b 3 4 5 b a b c a 6 7 String Matching basado en AF Complejidad temporal O(m |∑|(m+1)m) #iteraciones repeat # caracteres del patrón O(m3 |∑|) String Matching Algoritmo de Knuth-Morris-Pratt Se basa en un pre-procesamiento del patrón que evita el cálculo de la función de transición δ usando una función prefijo para patrones π precalculada desde el patrón. Informalmente, para cada q=0,1,…,m y cualquier a∈∑, π[q] contiene la información que es independiente de a y necesaria para calcular δ(q,a). String Matching Algoritmo de Knuth-Morris-Pratt T Si se logró un matching de q=5 P símbolos con desplazamiento s, se sabe que podrían coincidir símbolos con desplazamiento s+2 y no en s+1 s q s’ = s +2 k Pq Basta preprocesar el patrón! T[s+1..s+q] = P[1..q] Pk String Matching Algoritmo de Knuth-Morris-Pratt Teniendo en cuenta que P[1..q] = T[s+1..s+q], cuál es el menor desplazamiento s’ > s tal que P[1..k] = T[s+1..s’+k], donde s’+k = s+q? s’ es el primer desplazamiento que no es inválido. En la mejor situación, s’=s+q y los desplazamientos s+1, s+2, s+q-1 se excluyen. Esta información puede calcularse comparando al patrón contra sí mismo! String Matching Algoritmo de Knuth-Morris-Pratt ¿Cuál es el menor desplazamiento s’ > s tal que P[1..k] = T[s+1..s’+k], donde s’+k = s+q? ¿Cuál es el mayor k<q / Pk ⊐ Pq? Luego, s’=s +(q-k) es el siguiente desplazamiento potencialmente válido. String Matching Algoritmo de Knuth-Morris-Pratt Preprocesamiento del patrón Dado un patrón P[1..m], la función prefijo del patrón P es la función π: {1,2,..m}-> {0,1,..m-1}/ π[q] = max {k: k < q and Pk ⊐ Pq} Es decir, π[q] es la longitud del prefijo más largo de P que es sufijo de Pq . P ababaca π 0012301 String Matching Algoritmo de Knuth-Morris-Pratt KMP-MATCHER (T,P) { n = long (T); m= long (P); Π=COMPUTE-PREFIX-FUNCTION (P) { q=0; for (i=1;i<=n; i++) while (q >0 and P[q+1] <> T[i] q = Π[q]; if (P[q+1] == T[i]) q=q+1; if (q=m) cout <<“matching con desplazamiento”<<i-m; q = Π[q]; } String Matching Algoritmo de Knuth-Morris-Pratt COMPUTE-PREFIX-FUNCTION(P) 1 {m= long (P); 2 Π(1) = 0; 3 k = 0; 4 for (q=2;i<=m; q++) 5 while (k >0 and P[k+1] <> P[q] 6 k =Π[k]; 7 if (P[k+1] = P[q]) 8 k=k+1; 9 Π[q]= k; 10 return Π } String Matching Algoritmo de Knuth-Morris-Pratt String Matching Algoritmo de Knuth-Morris-Pratt P= ababababca m=10 (1) π[1]=0 (2) k=0 (3) q=2 (4) π[2]=0 (5,7,8) q=3 (4) k=1 (6) π[3]=1 (8)….. π[4]=2; π[5]=3; π[6]=4; π[7]=5; π[8]=6; π[9]=0; π[10]=1 String Matching Algoritmo de Knuth-Morris-Pratt Complejidad temporal Compute-Prefix-Function ∈ O(m) Knuth-Morris-Pratt ∈ O(m+n) Tiempo Preprocesamiento Autómata finito O(m | |) Knuth-Morris-Pratt O(m) Tiempo Matching O(n) O(n)