Algoritmos para strings Bioinformática 12-2-16 Elvira Mayordomo Hoy … Notación básica para strings El problema de string matching: String matching automata Boyer-Moore Árboles de sufijos (con otras aplicaciones) Vectores de sufijos Basic bibliography D. Gusfield: Algorithms on Strings, Trees, and Sequences. Cambridge University Press, 1997. T.H. Cormen, C.E. Leiserson, R.L. Rivest, C. Stein: Introduction to Algorithms (3rd ed.). MIT Press, 2009. Notación Alfabeto Símbolo (carácter, letra) String (secuencia, cadena) s Longitud de s, |s| Cadena vacía n * s[i] s[i,j] Definiciones básicas Concatenación s.t s es un substring de t si t=usv (algún u,v) s es un prefijo de t si t=sv (para algún v) s es un sufijo de t si t=us (para algún u) s es un substring (prefijo, sufijo) propio de t si s es un substring (prefijo, sufijo) de t y s t s es una subsecuencia de t si todos los símbolos de s aparecen en el mismo orden en t (pero no necesariamente seguidos) “Overlap” de s y t El solape de s y t es el string y que cumple: 1. 2. 3. y es sufijo de s, (s=xy) y es prefijo de t, (t=yz) |y| es máximo cumpliendo 1. y 2. Ov(s,t)=y <s,t>= xyz, la mezcla de s y t Pref(s,t)=x, Suff(s,t)=z ov(s,t)= |Ov(s,t)|, pref(s,t)=|Pref(s,t)|, |Suff(s,t)| Ov(s,s) suff(s,t)= Algoritmos para strings … Comparar, encontrar repeticiones … Algunos los usaremos directamente Buscar repeticiones exactas nos ayudará en problemas más complicados El problema del string matching Consiste en encontrar un string (corto), el patrón, como substring de un string (largo), el texto En bioinformática lo más frecuente es buscar un fragmento nuevo de DNA (un gen) en una colección de secuencias En este caso permitimos un cierto error, pero el pattern matching exacto es una subrutina Enunciado del problema … Entrada: Dos strings t= t1 … tn, p= p1 … pm sobre Salida: El conjunto de posiciones de t donde aparece p, es decir, I {1,…, n-m+1} tales que i I sii ti … ti+m-1 =p Algoritmo inocente … Input:A pattern p=p1. . .pm and a text t=t1. . .tn. I:=∅ for j:=0 to n-m do i:=1 while pi = tj+i and i m do i:=i+1 if i=m+1 then {p1 … pm = tj+1 … tj+m} I:=I{j+1} Output: The set I of positions, where an occurrence of p as a substring in t starts. Ejemplo … Algoritmo inocente … ¿Complejidad? ¿Casos peores? Buscamos mejorar esto explotando la estructura del patrón t= ababb p=abb Resto de métodos Preprocesar el patrón Preprocesar el texto (árboles de sufijos) Preprocesar el patrón: String matching automata Vamos a ver una solución en la que una vez preprocesado p en tiempo O(|p|.||) resolvemos el problema en una sola pasada de t Usa autómatas finitos … Autómata finito (DFA) Un DFA es una 5-tupla (Q, , q0,, F) donde: 1) Q : conjunto de estados 2) : alfabeto de entrada 4) q0 Q : estado inicial 3) : función de transición :QxQ 5) F Q : conjunto de estados finales (o de aceptación) Ejemplo de autómata Aceptar El autómata acepta un string x si empezando en q0 y siguiendo llega a un estado final … String matching automata Dado un patrón p=p1. . .pm construimos un autómata que acepte los textos con sufijo p (es decir, los textos que acaban en p) Ejemplo p=aba t= bababaa Cada vez que encuentro aba llego al estado final 3 ¿Por qué funciona? Ir al estado i quiere decir p1. . .pi es “el prefijo más largo de p que es sufijo de lo que llevo leído del texto” O sea, si estoy en el estado i, lo que llevo leído del texto termina en p1. . .pi Cómo construir el autómata para un patrón (recordad) El solape de s y t es el string y que cumple: 1. y es sufijo de s, (s=xy) 2. y es prefijo de t, (t=yz) 3. |y| es máximo cumpliendo 1. y 2. Ov(s,t)=y ov(s,t)= |Ov(s,t)|, Cómo construir el autómata para un patrón Dado un patrón p=p1. . .pm definimos el string matching autómata Mp con estados Q={0,1,…,m}, estado inicial q0=0, un único estado final m (F={m}) y la transición (i,a) = ov(p1. . .pia, p) para cada símbolo a y estado i (i,a) = ov(p1. . .pia, p)=“(la longitud d)el prefijo más largo de p que es sufijo de p1. . .pia” Si estoy en el estado i, lo que llevo leído del texto termina en p1. . .pi Por qué funciona … Se puede probar por inducción: Si empiezo en el estado inicial 0 y leo t1…tk llego al estado i que cumple: p 1. . .pi = ov(t1…tk, p) p1. . .pi es “el prefijo más largo de p que es sufijo de t1…tk” Por qué funciona … Si empiezo en el estado inicial 0 y leo t1…tk llego al estado i que cumple: p1. . .pi es “el prefijo más largo de p que es sufijo de t1…tk” Luego hay una ocurrencia de p en posición km+1 si p= p1. . .pm es sufijo de t1…tk, es decir, Si empiezo en el estado inicial 0 y leo t1…tk llego al estado m Algoritmo para construir el autómata String matching con autómata Complejidad Construir el autómata: O(||.m2) String matching: O(n) Se puede construir el autómata en tiempo O(||.m) Enunciado del problema … Entrada: Dos strings t= t1 … tn, p= p1 … pm sobre Salida: El conjunto de posiciones de t donde aparece p, es decir, I {1,…, n-m+1} tales que i I sii ti … ti+m-1 =p Vistos Algoritmo inocente: O(m.(n-m)) String matching autómata: O(||.m) preprocessing of pattern O(n) string matching Algoritmo de Boyer-Moore Se trata de utilizar el algoritmo “inocente”: ir moviendo patrón sobre el texto Pero … Cada comparación de p1…pm con tj+1…tj+m la hacemos empezando por el final Si podemos movemos más de una posición la j Usa preprocesamiento de p Ejemplo del inocente … Para mover más de una posición 1. Regla del carácter malo: Si estoy comparando p1…pm con tj+1…tj+m empezando por pm con tj+m, si encuentro pi distinto de tj+i, Muevo el p hacia la derecha hasta la última vez que aparece el símbolo tj+i en p (es decir, la primera vez por la derecha) Regla del carácter malo Preprocesamiento para la regla del carácter malo (a)= última ocurrencia de a en p Preprocesamiento para la regla del carácter malo Si estoy comparando p1…pm con tj+1…tj+m empezando por pm con tj+m, si encuentro pi distinto de tj+i=a, muevo el patrón i-(a) posiciones a la dcha del texto Coste del preprocesamiento O(m+||) Para mover más de una posición 2. Regla del buen sufijo: Si estoy comparando p1…pm con tj+1…tj+m empezando por pm con tj+m, si encuentro pi-1 distinto de tj+i-1, Muevo el p hacia la derecha hasta la siguiente ocurrencia de pi…pm en p Regla del buen sufijo Para mover más de una posición 2. Regla del buen sufijo PLUS: Si estoy comparando p1…pm con tj+1…tj+m empezando por pm con tj+m, si encuentro pi-1 distinto de tj+i-1, Muevo el p hacia la derecha hasta la siguiente ocurrencia de pi…pm en p Si no aparece muevo al mayor k tal que p1…pk es sufijo de pi…pm (i) es dónde colocar pm Regla del buen sufijo PLUS Cómo calcular … La siguiente ocurrencia de pi…pm en p (Muevo el p hacia la derecha hasta la siguiente ocurrencia de pi…pm en p) Es como la segunda ocurrencia de (pi…pm)R en pR Es como la primera ocurrencia de (pi…pm)R=pm…pi en (p1…pm-1)R=pm-1…p1 Para calcular … La primera ocurrencia de pm…pi en pm-1…p1 uso el autómata string matching con patrón pm…pi Es como el de patrón pm…p2 pero con estado final distinto (estado final m-i+1) Además (regla del buen sufijo PLUS) Si no aparece (la siguiente ocurrencia de pi…pm en p) muevo al mayor k tal que p1…pk es sufijo de pi…pm El último estado de buscar pm…p2 en pm-1…p1 nos dice: Si hemos llegado al estado m-j+1, eso quiere decir que pm…pj es sufijo de pm-1…p1 pm…pj-1 NO es sufijo de pm-1…p1 j es el menor tal que pj…pm es prefijo de p1…pm-1 k=m-j+1 es el mayor tal que p1…pk es sufijo de p2…pm Para i<=j, k=m-j+1 es el mayor tal que p1…pk es sufijo de pi…pm Complejidad buen sufijo Podemos hacer todo el preprocesamiento con el string matching autómata para pm…p2 usándolo una sola vez con entrada pm-1…p1 Coste de calcular el autómata: O(||.m) Usarlo con entrada pm-1…p1 : O(m) Complejidad de Boyer-Moore Preprocesamiento O(||.m) Caso peor: igual que el inocente O(||.m+n.m) Bastante rápido en la práctica Se puede mejorar el preproceso para que el caso peor sea O(n+m) Comparando complejidades Caso peor … Algoritmo inocente O(m.(n-m)) String matching automata O(n+||.m2), O(n+||.m) Boyer-Moore O(||.m+n.m), O(n+m+||) El caso peor del Boyer-Moore ocurre poco Alternativa: el Knuth-Morris-Pratt (mejor en el caso peor, peor en la práctica) Separando el preprocesamiento … Algoritmo inocente: O(m.(n-m)) String matching autómata: O(||.m) preprocesamiento del patrón O(n) string matching Boyer-Moore O(||.m) preprocesamiento del patrón , mejorado O(||+m) O(n.m) string matching, mejorado O(n) El próximo día … El problema de string matching: String matching automata Boyer-Moore Árboles de sufijos (con otras aplicaciones) Vectores de sufijos