Tema 11: Algoritmos de empate de cadenas M. en C. Edgardo Adrián Franco Martínez http://www.eafranco.com edfrancom@ipn.mx @edfrancom edgardoadrianfrancom 1
Contenido Introducción Empate de cadenas Fuerza bruta Complejidad de Fuerza bruta Rabin-Karp Complejidad de Rabin-Karp Knuth-Morris-Pratt Complejidad de Knuth-Morris-Pratt Autómata finito Complejidad de un autómata finito 2
Introducción Una cadena es una secuencia de caracteres sobre un alfabeto finito. ATCTAGAGA es uncadena sobre Σ = A, C, G, T El problema de emparejamiento de cadenas es encontrar todas las ocurrencias de una cadena p, llamada patrón, en una cadena más grande T del mismo alfabeto. Dadas las cadenas x, y y z, se dice que x es: a) Un prefijo de xy, b) Un sufijo de yx, c) Una subcadena de yxz. 3
A menudo sucede que los datos a procesar no se descomponen lógicamente en registros independientes que representen pequeñas partes identificables. Este tipo de datos se caracteriza fácilmente por el hecho de que se pueden escribir en forma de cadenas: series lineales (por lo regular muy largas) de caracteres. Las cadenas son evidentemente el centro de los sistemas de tratamiento de texto, que proporcionan una gran variedad de posibilidades para la manipulación de textos. 4
Tales sistemas procesan cadenas alfanuméricas, que pueden definirse en primera aproximación como series de letras, números y caracteres especiales. Estos objetos pueden ser bastante grandes, por lo que es importante disponer de algoritmos eficaces para su manipulación. Otro tipo de cadena es la cadena binaria, que es una simple serie de valores 0 y 1. Esta es, en cierto sentido, un tipo especial de cadena alfanumérica, pero es útil hacer la distinción porque existen diferentes algoritmos específicos para este tipo de cadenas y porque las cadenas binarias se utilizan en muchas aplicaciones. 5
Empate de cadenas Implica la implementación de algoritmos de búsqueda de subcadenas y por lo tanto su objetivo es buscar la existencia de una subcadena dentro de una cadena. La mayoría de los algoritmos para este problema se pueden modificar fácilmente para encontrar todas las ocurrencias del patrón en el texto, puesto que recorren el texto en secuencia y se pueden reinicializar en la posición situada inmediatamente después del comienzo de una concordancia, para encontrar la concordancia siguiente. 6
Fuerza bruta El método en el que se piensa de inmediato para el reconocimiento de patrones consiste simplemente en verificar, para cada posición posible del texto en la que el patrón pueda concordar, si efectivamente lo hace. 100111010010100010100111000111 Reconocer la cadena: 10100111 100111010010100010100111000111 10100111 10100111 10100111 10100111 10100111 10100111 100111010010100010100111000111 7
El algoritmo de Fuerza Bruta compara el patrón con el texto un carácter cada vez, hasta encontrar que no coinciden los caracteres. 8
Complejidad de Fuerza bruta Dado un patrón de M caracteres de longitud, y un texto de N caracteres de longitud. Mejor caso: encuentra el patrón en las primeras M posiciones del texto. Complejidad: O(N) Peor caso: longitud M. Complejidad: O(MN) compara el patrón con cada subcadena de texto de BruteForceSearch (T; P) for (s=0; s <= n-m; s++) if (P[1..m] == T[s+1..s+m]) cout << Matching << s; 9
Rabin-Karp Calcula un valor hash para el patrón, y para cada subsecuencia de M-caracteres de texto. Si los valores hash son diferentes, se calcula una valor para la siguiente secuencia. Si los valores hash son iguales se usa una comparación de Fuerza Bruta. Valor Hash de AAAAA es 37 Valor Hash de AAAAH es 100 10
Complejidad de Rabin-Karp Dado un patrón de M caracteres de longitud, y un texto de N caracteres de longitud. Complejidad Pre-procesamiento: O(M) Complejidad Búsqueda: O(MN) RabinKarpSearch(T, P) for( i=1; i<m; i++ ) dm = (dm * D) % Q; // Hash al patrón for( i=1; i<=m; i++ ) h1 = ((h1 * D) + pat[i] ) % Q; h2 = ((h2 * D) + text[i] ) % Q; for( i = 1; i <= n-m+1; i++ ) // Busqueda if( h1 == h2 ) // Potencial coincidencia for(j=1; j<=m && text[i-1+j] == pat[j]; j++ ); if( j > m ) // Coincidencia confirmada cout << Matching <<i; h2 = (h2 + (Q * D) - text[i]*dm ) % Q; h2 = ((h2 * D) + text[i+m] ) % Q; 11
Algoritmo Knuth-Morris-Pratt El algoritmo de búsqueda Knuth-Morris-Pratt (KMP) se diferencia del método de fuerza bruta porque mantiene una pista de información obtenida en comparaciones previas. Se calcula una función de fallo (f) que brinda información sobre el patrón a la hora de hacer las comparaciones. Permite saber el corrimiento sobre el patrón hasta la próxima comparación con algún carácter en el texto. 12
La idea básica de este algoritmo es que cuando se detecta una discordancia (no concordancia), el <falso> principio se compone de los caracteres que se conocen por adelantado (puesto que están en el patrón). Cuando se detecta la no concordancia, se sabe, en virtud del hecho de que concuerdan j caracteres, que no se necesita <retroceder> el puntero i del texto, puesto que ninguno de los j-1 caracteres del texto pueden concordar con el primer carácter del patrón. Saltar todos los caracteres del patrón cuando se detecta una discordancia, sería un error en el caso en el que el patrón se repita en el propio punto de la concordancia. 13
Complejidad: O(n + m) 14
Complejidad de Knuth-Morris-Pratt Dado un patrón de M caracteres de longitud, y un texto de N caracteres de longitud. Complejidad del calculo de la tabla de fallos: O(M) Complejidad Búsqueda: O(M+N) KMPSearch(T, P) KMPnext = prekmpfunction(p); while (r < n) while (h >= 0 && (P[h]!= T[r])) h = KMPnext[h]; h++; r++; if (h >= m) cout<< Matching << r - h; h = KMPnext [h]; 15
Algoritmo con autómata finito Un autómata finito (AF) o máquina de estado finito es un modelo computacional que realiza cómputos en forma automática sobre una entrada para producir una salida. Este modelo está conformado por un alfabeto, un conjunto de estados y un conjunto de transiciones entre dichos estados. Su funcionamiento se basa en una función de transición, que recibe a partir de un estado inicial una cadena de caracteres pertenecientes al alfabeto (la entrada), y que va leyendo dicha cadena a medida que el autómata se desplaza de un estado a otro, para finalmente detenerse en un estado final o de aceptación, que representa la salida. 16
Complejidad de la búsqueda con autómata finito Utiliza un AF Determinístico Cantidad de Estados = m+1 Cantidad de Comparaciones = n Complejidad Pre-procesamiento: O(m³ Σ )) Complejidad Búsqueda: O(n) AutomataFinitoSearch(T; P;d) estado = automata->getinicial(); for(j = 0; j <= n; j++ ) estado = d(estado, T [j]); if (automata->esfinal(estado)) cout << Matching << j + 1 - m; 17