String Matching

Anuncio
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)
Descargar