fbpx
Wikipedia

Algoritmo Knuth-Morris-Pratt

El algoritmo KMP es un algoritmo de búsqueda de subcadenas simple. Por lo tanto su objetivo es buscar la existencia de una subcadena (habitualmente llamada patrón) dentro de otra cadena. La búsqueda se lleva a cabo utilizando información basada en los fallos previos. Información obtenida del propio patrón creando previamente una tabla de valores sobre su propio contenido. El objetivo de crear dicha tabla es poder determinar donde podría darse la siguiente existencia, sin necesidad de analizar más de 1 vez los caracteres de la cadena donde se busca.

En 1970 S.A. Cook sugirió la existencia de un algoritmo que resuelve la búsqueda en un tiempo equivalente a M+N, en el peor caso, a raíz de unos resultados teóricos que obtuvo mientras probaba un tipo de máquina abstracta.[1]Knuth y Pratt siguiendo los pasos que Cook usó para probar su teorema, lograron crear el algoritmo que lleva sus nombres. De modo independiente Morris mientras trabajaba en un editor de texto acabó descubriendo el mismo algoritmo, para satisfacer la cuestión de la búsqueda eficiente de subcadenas, y que publicaron juntos los tres en 1976.

Descripción del algoritmo KMP

El algoritmo KMP trata de localizar la posición de comienzo de una cadena dentro de otra. Previamente sobre el patrón a localizar se calcula una tabla de saltos (conocida como tabla de fallos) que después se utiliza (al examinar las cadenas) para hacer saltos cuando se localiza un fallo de coincidencia en la comparación de un carácter.

Supongamos una tabla 'F' ya precalculada, y supongamos que el patrón a buscar esté contenido en la matriz 'P()', y la cadena donde buscamos esté contenida en la matriz 'T()'. Entonces ambas cadenas comienzan a compararse usando un puntero de avance para el patrón, si ocurre un fallo (de coincidencia), el puntero salta hacia donde indica el valor en la posición del puntero actual sobre la tabla de fallos . El array 'T' utiliza un puntero de avance absoluto que determina el comienzo de una sección del mismo tamaño que el patrón. Dentro de dicha sección se desplaza con el puntero del patrón, analizando cada carácter en la sección con cada carácter en el patrón. Se dan 2 situaciones:

Mientras existan coincidencias el puntero de avance de 'P', se va incrementando (apunta al siguiente par de caracteres a comparar entre el patrón y la sección actual en el array 'T') y si alcanza el final del patrón se devuelve la posición actual del puntero absoluto del array 'T'.
Si se da un fallo, la sección del array 'T' (el puntero absoluto de avance) se actualiza, sumando el valor del puntero de 'P' + el valor que contiene la tabla 'F' en el mismo índice que 'P'. Además de actualizar la sección bajo examen, debe igualmente alinearse el patrón bajo dicha sección, para coincidir bajo una de 2 circunstancias; Si el valor de 'F' es mayor que -1 el puntero de 'P', toma el valor que indica la tabla de salto 'F', en caso contrario vuelve a recomenzar su valor en 0 (comienzo del patrón y de la siguiente sección).

Pseudocódigo del algoritmo

En el pseudocódigo no se incluye la verificación de las cadenas vacías.

 Algoritmo BúsquedaKMP: Entrada: un array de caracteres, T (el texto donde se busca) un array de caracteres, P (la palabra/s (patrón) que se busca) Salida: un entero  (que expresa la posición en T en la cual se encontró P) Variables en juego: un entero, k ← 0  (posición absoluta de la sección bajo examen en el array T) un entero, i ← 0  (posición absoluta del carácter bajo examen en el array P) un entero, m ← size(T) (Cantidad de caracteres en el array T) un entero, n ← size(P) (cantidad de caracteres en el array P) un array de enteros, F (la tabla de fallo, calculada previamente o a continuación) Código: Si (m => n) F = TablaKMP(P) (calcula la tabla de fallos) En Bucle Mientras ((i < n) y ((k + i) < m) ) Si P[i] = T[k + i]  Asignar i ← (i + 1) Si no  Asignar k ← (k + i - F[i]) Si (i > 0) Asignar i ← F[i] Fin si Repetir Fin si  Si (i = n) Devolver k si no Devolver m  (o devolver -1, si se usa un entero con signo) Fin si 

Descripción de la tabla adicional (conocida como 'función de fallo')

El objetivo de la tabla (precalculada) de fallo 'F' es no permitir que cada carácter del array 'T()' sea examinado más de 1 vez. El método clave para lograr esto, consiste en haber comprobado algún trozo de la cadena donde se busca con algún trozo de la cadena que se busca, lo que nos proporciona en qué sitios potenciales puede existir una nueva coincidencia, sobre el sector analizado que indica fallo.

Dicho de otro modo, partiendo del patrón a buscar, se localiza sobre sí mismo que partes se repiten enteramente dentro del patrón desde el comienzo y para ello se elabora una lista con todas las posiciones, de salto atrás que señalen cuanto se retrocede desde la posición actual (que ya hayan coincido y luego fallado) del patrón. Por ejemplo si el texto a buscar es 'posponer' y estamos examinando un texto como 'el presente texto es pospositivo mientras nos falte el ingenio', cuando llegamos a encontrar 'pospo'sitivo coincidente con 'pospo'ner, tras esa 2ª 'po', en el patrón ahora se espera encontrar una 'n', que falla, pues aparece una 's' en el array 'T', la pregunta lógica sería ¿ dónde se encuentra de nuevo (si existe) la primera letra en el texto (antes del fallo), y hasta donde logra repetirse ?. La respuesta a esta pregunta será el punto de salto, en el caso de ejemplo (el patrón 'posponer'). Dicho punto nos lo 'susurra' la tabla de fallos en la posición que falla 'n' (posición 5), con un valor de 3, que viene a decir que coinciden tres caracteres desde el comienzo hasta justo antes del fallo, luego puede hacerse un salto para alinear 'pos'poner con pos'pos'itivo, y continuar comparando desde 'p' con 'i' respectivamente en cada palabra.

Por tanto esta tabla se confecciona con la distancia que existe desde un punto en la palabra a la última ocurrencia (de la 0ª letra de la palabra) distinta de la primera vez que aparece, y mientras sigan coincidiendo, se marca la distancia, cuando haya una ruptura de coincidencia se marca 0, y así sucesivamente hasta terminar con el texto.

La tabla tiene sus 2 primeros valores fijados, de modo que la función de fallo empieza siempre examinando el 3 carácter del texto. La razón por la que dichos valores están fijados es obvia: si para el 2º carácter se marcara 1, nunca se lograría un salto, pues siempre retornaría a dicho punto. En cuanto al primero, por necesidad se marca -1, pues de ese modo le es imposible regresar más atrás, sino siempre adelante (el valor que contiene la tabla siempre se usa restando en la función de búsqueda, luego restar un -1, supone en efecto sumar 1 y por tanto avanza).

Es importante notar que las únicas repeticiones que cuentan son aquellas que se repiten enteramente desde el comienzo del patrón. Por ejemplo, si el patrón fuera 'cateter', que aparezca repetido las letras 'te' más de una vez carece de importancia, pues la segunda vez no va precedido de forma continua desde el comienzo, como podría ser en la palabra ficticia 'catequiscater'.

Ejemplos de un patrón y la creación de su tabla de fallo

Primero un sencillo ejemplo donde hay una única ocurrencia.

'01234567 'TANGENTE' -10000001' Se busca la 1ª 'T', que aparece tras la de comienzo, ocurre en la posición 6, la siguiente letra a la 'T' hallada, la 'e' está a una distancia de 1 de esta aparición de 'T', por ello se marca con un 1. 

Ahora un ejemplo más interesante

'0123456789012 'MAREMAGNUM EL' -1000012000100 Hay 2 ocurrencias con 'MA' y más adelante otra con 'M' 

Y terminamos con un ejemplo de 4 coincidencias, 2 de 3 letras consecutivas 'PAR' y la 3ª de 6 letras 'PARTIC'. Se ha coloreado los tramos coincidentes, para más claridad.

 0 10 20 30 40 <-Posiciones de los caracteres en tramos de 10. '01234567890123456789012345678901234567890' - + + +  <- Marcando posiciones donde, aparece la 1ª letra. 'PARTICIPARIA CON MI PARACAIDAS PARTICULAR' <- Texto a calcular su tabla de fallos. -100000001230000000000 La 'P' es la 0ª letra, luego en la posición 7 vuelve a aparecer, por tanto en la 8ª posición, se marca un '1' (la distancia a la 'P' hallada, mientras continúe coincidiendo), luego coinciden las 2 siguientes letras, entonces se marca la distancia, luego falla, hay una 'A' y se esperaba una 'T', entonces se marca '0' hasta que vuelva a aparecer de nuevo la 1ª letra del texto 'P'. 'PARTICIPARIA CON MI PARACAIDAS PARTICULAR'   12300000000 Lo cual sucede otra vez en la posición 20, por tanto en la 21, se marca '1', de nuevo coinciden tras la 'P', la 'A' y la 'R', pero la 24ª falla, es una 'C', no una 'T', de nuevo se marca con '0', hasta que se dé otra ocurrencia del comienzo del texto. 'PARTICIPARIA CON MI PARACAIDAS PARTICULAR'   123456 En la posición 31.ª, vuelve a aparecer una 'P', se marca en la siguiente la distancia '1', coincide también la siguiente , marcamos '2' y así correlativamente mientras exista correspondencia entre la examinada y la correspondiente del inicio... en total se encuentra coincidencia entre ambos tramos en 'PARTIC', de ahí que marquemos '0123456' y volvemos a marcar '0' otra vez ante el fallo. Finalmente tenemos nuestra tabla de fallos: 'PARTICIPARIA CON MI PARACAIDAS PARTICULAR' -10000000123000000000012300000000123456000 

Pseudocódigo de la tabla (función de fallo)

 Algoritmo TablaKMP: Entrada: un array de caracteres, P (la palabra/patrón/texto que va a ser analizada)  Salida: un array de enteros, F (la tabla de fallos a rellenar) del mismo tamaño que P. Variables en juego: un entero, pos ← 2 (la posición actual donde se está calculando F) un entero, cnd ← 0 (el índice en P del siguiente carácter del candidato actuala) un entero, n ← Size(p) (cantidad de caracteres en el patrón) Código:  (algunos valores se fijan con determinado valor) Asignar F[0] ← -1, F[1] ← 0 (ver explicación más arriba, en el apartado de la descripción) En Bucle Mientras (pos => n)  Si P[pos - 1] = P[cnd] (caso 1º: siguiente candidato coincidente en la cadena)  Asignar cnd ← (cnd + 1), F[pos] ← cnd, pos ← (pos + 1)  Pero si (cnd > 0) (caso 2º: con fallo de coincidencias consecutivas, se asigna un valor ya conocido)  Asignar cnd ← F[cnd]  Y si no  (caso 3º: no se halló candidatos coincidentes (otra vez))  Asignar F[pos] ← 0, pos ← (pos + 1) Fin si Repetir Devolver F  (si no se recibió por referencia) 

Eficiencia del algoritmo

Debido a que el algoritmo precisa de 2 partes donde se analiza una cadena en cada parte, la complejidad promedio resultante es O(k) y O(n), cuya suma resulta ser O(n + k).

Genéricamente puede despreciarse el tiempo de cálculo de la tabla de fallos del patrón, salvo que su tamaño sea excesivamente grande. También en los casos en que el mismo patrón se buscará en muchos diferentes textos y que es la razón por la que conviene precalcular la tabla de fallos del patrón de forma independiente de la función de búsqueda.

La capacidad del algoritmo (para demostrar que no examina caracteres más de dos veces) queda patente al apreciar como logra saltar varios caracteres a la vez cuando hay un fallo. Esto se aprecia mejor, mirando la tabla 'F', cuantos mayor es el valor en la tabla tanto más grande es el salto resultante (salto debido a que dichos caracteres ya han sido examinados) y cuantos más ceros haya, señala que ha ido avanzando el puntero absoluto de uno en uno.

En la práctica el algoritmo no será mucho mejor que el algoritmo lineal de fuerza bruta, en condicioes normales (texto del lenguaje hablado, por ejemplo), pero mejora sustancialmente, respecto del mismo cuando sale de dichas condiciones normales, en tanto que algoritmo presente no presenta demasiada sobrecarga.

Casos peor y mejor para el patrón

Respecto del patrón, no existe caso mejor ni peor (considerado por sí solo), puede demostrarse que en una tabla de fallos donde son todo ceros (o lo que es lo mismo, el primer carácter del patrón aparece una única vez (por ejemplo P ="ABBBBBBB")), tiene el mismo coste que cuando el patrón se compone de 2 únicos caracteres donde 1 se repite en todo el patrón y el otro aparece al final, (por ejemplo: P= "AAAAAAAB", (obsérvese la tabla F para dicho texto)) y tiene el mismo coste que en cualquier otra situación intermedia entre dichos extremos:

i 0 1 2 3 4 5 6 7
P[i] A A A A A A A B
F[i] -1 0 1 2 3 4 5 6

En el caso de que el primer carácter aparezca una única vez (ver tabla aquí debajo), examina la A, si coincide, examina la B, en caso de fallo simplemente salta 1 carácter. Si el patrón existiere finalmente en el texto, será cuando haya oportunidad de examinar el resto de caracteres, antes de alcanzar el final del texto.

i 0 1 2 3 4 5 6 7
P[i] A B B B B B B B
F[i] -1 0 0 0 0 0 0 0

Igualmente cuando el primer carácter se repite excepto en el último carácter. Recorrería, hasta el último que genera el fallo, pero igualmente el puntero absoluto del texto, solo avanza 1, con cada fallo.

La diferencia entre uno y otro casos consiste básicamente en donde se localiza el puntero en el patrón y en qué momento se recorre el resto del patrón si al principio o al final del texto.

Casos peor y mejor para el texto

Para el texto, el mejor caso se da cuando el patrón se localiza en la posición 0. El peor caso se da cuando el patrón se localiza en la última posición. Si el texto no se encuentra, es igual o ligeramente mejor que el peor caso, y es en tal caso dependiente del patrón (ver sección anterior y siguiente).

Casos peor y mejor en la búsqueda

Considerando conjuntamente el patrón y el texto, el peor caso se puede dar en función de donde y cuantas veces surja la ruptura del patrón. Cada carácter fallido necesita ser comprobado otra vez con aquel con el que se alinea. Esto implica que el peor caso puede llegar a tener un costo de O(n) + O(k*2), si bien en la práctica estos casos son raros, pues no es frecuente que deban realizarse búsqueda con patrones o textos cuyos caracteres tengan una alta frecuencia de reptición (como los que s emuestran de ejemplo a continuación)..

Ejemplos: Patrón (tabla F):Texto = nº de comparaciones precisas (en todos los ejemplos el tamaño del patrón y del texto es igual: 6:30)

AAAAAZ (-1  0  1  2  3  4):AAAAACAAAAACAAAAACAAAAACAAAAAZ = 51

AAAAAZ (-1  0  1  2  3  4):AAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ = 54

ABBBBB (-1  0  0  0  0  0):ABBBBZABBBBZABBBBZABBBBZABBBBB = 35 (coste típico m+n).

Ejemplo

Una forma de entender correctamente el funcionamiento del algoritmo es seguir paso a paso un ejemplo reseñando en cada punto lo que hace o puede hacer el algoritmo en una situación dada.

Se considera (para el presente artículo) que tanto en los ejemplos como en el pseudocódigo, los arrays de caracteres se basan en índice 0.

Sean 2 cadenas, que se entregan como arrays de caracteres a la función, donde T es el array de caracteres donde queremos buscar y P el array del patrón que se quiere hallar en T, usando k como puntero absoluto para los caracteres de T e i como puntero para los caracteres de P. Se usa también, una tabla (matriz) precalculada de la palabra a buscar F. A continuación se ve una figura que representan las variables consideradas para el ejemplo.

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F:-1000012 

Se muestra la tabla F, precalculada para el ejemplo.

i 0 1 2 3 4 5 6
P[i] A B C D A B D
F[i] -1 0 0 0 0 1 2


k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F:-1000012 

Comienza el cálculo, los punteros 'k' e 'i' inicialmente valen 0. Si P(i) = T(k + i), se evalúa a continuación si i = tamaño de P en cuyo caso se habría encontrado la posición de la cadena. En caso contrario, se incrementa 'i'. Esto sucede 3 veces, hasta que en la 4ª ocasión, en P(3) tenemos 'D' y en T(k + i) tenemos ' '(un espacio), momento en que actualizamos 'k', k = k + i - F(i) (k = 3). A continuación verificamos si F(i) > -1 (F(3) vale 0), como se da el caso hacemos para i = F(i). Es decir saltamos a la posición sobre el array 'P' que señala 'F(i)', que en este caso es 0, por tanto al principio del array 'P', pero ahora el puntero del array 'T' está en 3 (k = 3).

Por tanto en la siguiente figura avanzamos hasta la posición absoluta de T actual, 3.

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F: -1000012 

Volvemos al principio del bucle (cada vez que se vuelve a este punto se comprueba si 'k' alcanzó el tamaño de 'T' y de nuevo verificamos si P(i) = T(k + i). Vemos que en el punto actual en 'P' hay un espacio, por lo que, nuevamente se actualiza 'k', k = k + i - F(i) (k = 4), porque 'F(i) = -1'. Ahora entonces se hace i = 0 (aunque antes también era 0).

Como volvemos al inicio del bucle (se da por comprobado si el bucle finaliza), actualizamos la figura con el puntero en 'k', (k = 4)

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F: -1000012 

Ahora vemos que varias veces se cumple que P(i) = T(k + i), pero no tantas que se alcance el tamaño de 'P', y con cada coincidencia 'i' se ha incrementado, por lo que ahora 'i=6', pero se ha incrementado justo después de verificar si i = tamaño P luego devolver k (si no habríamos alcanzado la solución). Al analizar de nuevo al inicio del bucle la posición 6, falla ya que 'P(6) = D' y 'T(4 + 6) =' . en este punto toca actualizar 'k', y hacemos k = k + i - F(i) (k = 4 + 6 - F(6), en este punto F(6) vale 2, por lo que finalmente damos un salto 'k = 4 + 6 - 2 = 8'. Y actualizamos 'i', si F(6) > -1 luego i = F(i), es decir no solo acanzamos el puntero de 'T', sino que además avanzamos el puntero de 'P' a 2, porque precisamente en la posición 8, hay una coincidencia 'AB' tal como comienza la cadena del array 'w'. es justo e este punto donde hemos visto el salto y la eficacia de la tabla F.

Actualizamos la figura, damos por verificado la comprobación del inicio del bucle.

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F: -1000012 

Una nueva vez volvemos comprobar si P(i) = T(k + i) (P(2) = T(8+2)), es decir 'C' = ' '), como falla toca actualizar el puntero de 'T'. k = k + i - F(i) (k=8 + 2 - 0 = 10), y actualizamos 'i', (si F(i) > -1 luego i = F(i)) 'i = 0'.

Actualizamos a la siguiente figura (como cada vez que se actualiza 'k' el puntero absoluto de 'T').

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P:  ABCDABD i:  0123456 F: -1000012 

Con la siguiente comparación también falla ya que 'A' <> ' ', y nuevamente debe actualizarse el puntero 'k', con su salto de tabla (si procede), k = k + i - F(i) (k = 10 + 0 - (-1) = 11), y también actualizamos 'i', como esta vez 'F(i)= -1', entonces volvemos al principio de 'P', es decir i = 0.

Actualizamos la figura a la nueva posición que apunta el puntero de 'T', (k = 11)

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P: ABCDABD i: 0123456 F:  -1000012 

En esta ocasión de nuevo varias veces se cumplirá que P(i) = T(k +i), hasta que se llega a la posición de 'i = 6, que sucede que 'P(6)=D' que esdistinto de 'T(11 + 6)=C', por lo tanto es necesario una nueva bifurcación hacia la actualización de 'k', k = k + i - F(i) (k = 11 + 6 - 2 = 15). De nuevo entra en juego el salto de la tabla, puesto que 'F(i) = 2', toca actualizar 'i', si F(i) > -1 luego i = F(i), por tanto 'i = 2.

Actualizamos una vez más la figura, hasta avanzar hasta 'k = 15'.

k: 01234567890123456789012 T: ABC ABCDAB ABCDABCDABDE P:  ABCDABD i:  0123456 F:  -1000012 

Como 'i = 2' (porque conseguimos avanzar hasta el índice 6 que en la tabla vale 2, porque antes de la 'D' final hay 2 posiciones coincidentes consecutivamente), entonces ahora volvemos a hacer las comprobaciones pero ahora en 15 + 2, que era: Si P(i) = T(k + i) luego... si i = tamaño de w luego devolver k. Esta parte se cumple hasta finalmente encontrar por completo la cadena, antes de que 'i' pueda ser aumentado a 7 en la parte que sigue a la condición anterior i = i + 1 .


El algoritmo Knuth-Morris-Pratt (para este ejemplo) realiza 26 comparaciones hasta encontrar la solución, en la posición 15. Es necesario recordar que al estar basado en array el índice es 0 (frente a la habitual forma de contar caracteres desde la posición 1), aunque por otra parte, en las figuras se ve cómo los punteros de 'k' e 'i' empiezan en 0.

Véase también

Referencias

  1. Algorithms, de R. Sedgewick, Editorial Addison-Wesley Publishing Company, USA, 1983, página: 242. ISBN 0-201-06672-6.
  • Algorithms, R.Sedgewick - Ed. Addison-Wesley, 1983, página: 244 y siguientes, ISBN:0-201-06672-6
  • Data Structures & their Algorithms, H.R. Lewis y L. Deneberg - Ed.Harper Collins, 1991, página: 157 y siguientes, ISBN:0-673-39736-X
  •   Datos: Q45285

algoritmo, knuth, morris, pratt, algoritmo, algoritmo, búsqueda, subcadenas, simple, tanto, objetivo, buscar, existencia, subcadena, habitualmente, llamada, patrón, dentro, otra, cadena, búsqueda, lleva, cabo, utilizando, información, basada, fallos, previos, . El algoritmo KMP es un algoritmo de busqueda de subcadenas simple Por lo tanto su objetivo es buscar la existencia de una subcadena habitualmente llamada patron dentro de otra cadena La busqueda se lleva a cabo utilizando informacion basada en los fallos previos Informacion obtenida del propio patron creando previamente una tabla de valores sobre su propio contenido El objetivo de crear dicha tabla es poder determinar donde podria darse la siguiente existencia sin necesidad de analizar mas de 1 vez los caracteres de la cadena donde se busca En 1970 S A Cook sugirio la existencia de un algoritmo que resuelve la busqueda en un tiempo equivalente a M N en el peor caso a raiz de unos resultados teoricos que obtuvo mientras probaba un tipo de maquina abstracta 1 Knuth y Pratt siguiendo los pasos que Cook uso para probar su teorema lograron crear el algoritmo que lleva sus nombres De modo independiente Morris mientras trabajaba en un editor de texto acabo descubriendo el mismo algoritmo para satisfacer la cuestion de la busqueda eficiente de subcadenas y que publicaron juntos los tres en 1976 Indice 1 Descripcion del algoritmo KMP 2 Pseudocodigo del algoritmo 3 Descripcion de la tabla adicional conocida como funcion de fallo 4 Ejemplos de un patron y la creacion de su tabla de fallo 5 Pseudocodigo de la tabla funcion de fallo 6 Eficiencia del algoritmo 6 1 Casos peor y mejor para el patron 6 2 Casos peor y mejor para el texto 6 3 Casos peor y mejor en la busqueda 7 Ejemplo 8 Vease tambien 9 ReferenciasDescripcion del algoritmo KMP EditarEl algoritmo KMP trata de localizar la posicion de comienzo de una cadena dentro de otra Previamente sobre el patron a localizar se calcula una tabla de saltos conocida como tabla de fallos que despues se utiliza al examinar las cadenas para hacer saltos cuando se localiza un fallo de coincidencia en la comparacion de un caracter Supongamos una tabla F ya precalculada y supongamos que el patron a buscar este contenido en la matriz P y la cadena donde buscamos este contenida en la matriz T Entonces ambas cadenas comienzan a compararse usando un puntero de avance para el patron si ocurre un fallo de coincidencia el puntero salta hacia donde indica el valor en la posicion del puntero actual sobre la tabla de fallos El array T utiliza un puntero de avance absoluto que determina el comienzo de una seccion del mismo tamano que el patron Dentro de dicha seccion se desplaza con el puntero del patron analizando cada caracter en la seccion con cada caracter en el patron Se dan 2 situaciones Mientras existan coincidencias el puntero de avance de P se va incrementando apunta al siguiente par de caracteres a comparar entre el patron y la seccion actual en el array T y si alcanza el final del patron se devuelve la posicion actual del puntero absoluto del array T Si se da un fallo la seccion del array T el puntero absoluto de avance se actualiza sumando el valor del puntero de P el valor que contiene la tabla F en el mismo indice que P Ademas de actualizar la seccion bajo examen debe igualmente alinearse el patron bajo dicha seccion para coincidir bajo una de 2 circunstancias Si el valor de F es mayor que 1 el puntero de P toma el valor que indica la tabla de salto F en caso contrario vuelve a recomenzar su valor en 0 comienzo del patron y de la siguiente seccion Pseudocodigo del algoritmo EditarEn el pseudocodigo no se incluye la verificacion de las cadenas vacias Algoritmo BusquedaKMP Entrada un array de caracteres T el texto donde se busca un array de caracteres P la palabra s patron que se busca Salida un entero que expresa la posicion en T en la cual se encontro P Variables en juego un entero k 0 posicion absoluta de la seccion bajo examen en el array T un entero i 0 posicion absoluta del caracter bajo examen en el array P un entero m size T Cantidad de caracteres en el array T un entero n size P cantidad de caracteres en el array P un array de enteros F la tabla de fallo calculada previamente o a continuacion Codigo Si m gt n F TablaKMP P calcula la tabla de fallos En Bucle Mientras i lt n y k i lt m Si P i T k i Asignar i i 1 Si no Asignar k k i F i Si i gt 0 Asignar i F i Fin si Repetir Fin si Si i n Devolver k si no Devolver m o devolver 1 si se usa un entero con signo Fin siDescripcion de la tabla adicional conocida como funcion de fallo EditarEl objetivo de la tabla precalculada de fallo F es no permitir que cada caracter del array T sea examinado mas de 1 vez El metodo clave para lograr esto consiste en haber comprobado algun trozo de la cadena donde se busca con algun trozo de la cadena que se busca lo que nos proporciona en que sitios potenciales puede existir una nueva coincidencia sobre el sector analizado que indica fallo Dicho de otro modo partiendo del patron a buscar se localiza sobre si mismo que partes se repiten enteramente dentro del patron desde el comienzo y para ello se elabora una lista con todas las posiciones de salto atras que senalen cuanto se retrocede desde la posicion actual que ya hayan coincido y luego fallado del patron Por ejemplo si el texto a buscar es posponer y estamos examinando un texto como el presente texto es pospositivo mientras nos falte el ingenio cuando llegamos a encontrar pospo sitivo coincidente con pospo ner tras esa 2ª po en el patron ahora se espera encontrar una n que falla pues aparece una s en el array T la pregunta logica seria donde se encuentra de nuevo si existe la primera letra en el texto antes del fallo y hasta donde logra repetirse La respuesta a esta pregunta sera el punto de salto en el caso de ejemplo el patron posponer Dicho punto nos lo susurra la tabla de fallos en la posicion que falla n posicion 5 con un valor de 3 que viene a decir que coinciden tres caracteres desde el comienzo hasta justo antes del fallo luego puede hacerse un salto para alinear pos poner con pos pos itivo y continuar comparando desde p con i respectivamente en cada palabra Por tanto esta tabla se confecciona con la distancia que existe desde un punto en la palabra a la ultima ocurrencia de la 0ª letra de la palabra distinta de la primera vez que aparece y mientras sigan coincidiendo se marca la distancia cuando haya una ruptura de coincidencia se marca 0 y asi sucesivamente hasta terminar con el texto La tabla tiene sus 2 primeros valores fijados de modo que la funcion de fallo empieza siempre examinando el 3 caracter del texto La razon por la que dichos valores estan fijados es obvia si para el 2º caracter se marcara 1 nunca se lograria un salto pues siempre retornaria a dicho punto En cuanto al primero por necesidad se marca 1 pues de ese modo le es imposible regresar mas atras sino siempre adelante el valor que contiene la tabla siempre se usa restando en la funcion de busqueda luego restar un 1 supone en efecto sumar 1 y por tanto avanza Es importante notar que las unicas repeticiones que cuentan son aquellas que se repiten enteramente desde el comienzo del patron Por ejemplo si el patron fuera cateter que aparezca repetido las letras te mas de una vez carece de importancia pues la segunda vez no va precedido de forma continua desde el comienzo como podria ser en la palabra ficticia cate quiscate r Ejemplos de un patron y la creacion de su tabla de fallo EditarPrimero un sencillo ejemplo donde hay una unica ocurrencia 01234567 T ANGENT E 10000001 Se busca la 1ª T que aparece tras la de comienzo ocurre en la posicion 6 la siguiente letra a la T hallada la e esta a una distancia de 1 de esta aparicion de T por ello se marca con un 1 Ahora un ejemplo mas interesante 0123456789012 M AREMA GNUM EL 1000012000100 Hay 2 ocurrencias con MA y mas adelante otra con M Y terminamos con un ejemplo de 4 coincidencias 2 de 3 letras consecutivas PAR y la 3ª de 6 letras PARTIC Se ha coloreado los tramos coincidentes para mas claridad 0 10 20 30 40 lt Posiciones de los caracteres en tramos de 10 01234567890123456789012345678901234567890 lt Marcando posiciones donde aparece la 1ª letra PAR TICIPAR IA CON MI PARACAIDAS PARTICULAR lt Texto a calcular su tabla de fallos 100000001230000000000 La P es la 0ª letra luego en la posicion 7 vuelve a aparecer por tanto en la 8ª posicion se marca un 1 la distancia a la P hallada mientras continue coincidiendo luego coinciden las 2 siguientes letras entonces se marca la distancia luego falla hay una A y se esperaba una T entonces se marca 0 hasta que vuelva a aparecer de nuevo la 1ª letra del texto P PAR TICIPARIA CON MI PAR ACAIDAS PARTICULAR 12300000000 Lo cual sucede otra vez en la posicion 20 por tanto en la 21 se marca 1 de nuevo coinciden tras la P la A y la R pero la 24ª falla es una C no una T de nuevo se marca con 0 hasta que se de otra ocurrencia del comienzo del texto PARTIC IPARIA CON MI PARACAIDAS PARTIC ULAR 123456 En la posicion 31 ª vuelve a aparecer una P se marca en la siguiente la distancia 1 coincide tambien la siguiente marcamos 2 y asi correlativamente mientras exista correspondencia entre la examinada y la correspondiente del inicio en total se encuentra coincidencia entre ambos tramos en PARTIC de ahi que marquemos 0123456 y volvemos a marcar 0 otra vez ante el fallo Finalmente tenemos nuestra tabla de fallos PAR TIC IPAR IA CON MI PAR ACAIDAS PARTIC ULAR 10000000123000000000012300000000123456000Pseudocodigo de la tabla funcion de fallo EditarAlgoritmo TablaKMP Entrada un array de caracteres P la palabra patron texto que va a ser analizada Salida un array de enteros F la tabla de fallos a rellenar del mismo tamano que P Variables en juego un entero pos 2 la posicion actual donde se esta calculando F un entero cnd 0 el indice en P del siguiente caracter del candidato actuala un entero n Size p cantidad de caracteres en el patron Codigo algunos valores se fijan con determinado valor Asignar F 0 1 F 1 0 ver explicacion mas arriba en el apartado de la descripcion En Bucle Mientras pos gt n Si P pos 1 P cnd caso 1º siguiente candidato coincidente en la cadena Asignar cnd cnd 1 F pos cnd pos pos 1 Pero si cnd gt 0 caso 2º con fallo de coincidencias consecutivas se asigna un valor ya conocido Asignar cnd F cnd Y si no caso 3º no se hallo candidatos coincidentes otra vez Asignar F pos 0 pos pos 1 Fin si Repetir Devolver F si no se recibio por referencia Eficiencia del algoritmo EditarDebido a que el algoritmo precisa de 2 partes donde se analiza una cadena en cada parte la complejidad promedio resultante es O k y O n cuya suma resulta ser O n k Genericamente puede despreciarse el tiempo de calculo de la tabla de fallos del patron salvo que su tamano sea excesivamente grande Tambien en los casos en que el mismo patron se buscara en muchos diferentes textos y que es la razon por la que conviene precalcular la tabla de fallos del patron de forma independiente de la funcion de busqueda La capacidad del algoritmo para demostrar que no examina caracteres mas de dos veces queda patente al apreciar como logra saltar varios caracteres a la vez cuando hay un fallo Esto se aprecia mejor mirando la tabla F cuantos mayor es el valor en la tabla tanto mas grande es el salto resultante salto debido a que dichos caracteres ya han sido examinados y cuantos mas ceros haya senala que ha ido avanzando el puntero absoluto de uno en uno En la practica el algoritmo no sera mucho mejor que el algoritmo lineal de fuerza bruta en condicioes normales texto del lenguaje hablado por ejemplo pero mejora sustancialmente respecto del mismo cuando sale de dichas condiciones normales en tanto que algoritmo presente no presenta demasiada sobrecarga Casos peor y mejor para el patron Editar Respecto del patron no existe caso mejor ni peor considerado por si solo puede demostrarse que en una tabla de fallos donde son todo ceros o lo que es lo mismo el primer caracter del patron aparece una unica vez por ejemplo P ABBBBBBB tiene el mismo coste que cuando el patron se compone de 2 unicos caracteres donde 1 se repite en todo el patron y el otro aparece al final por ejemplo P AAAAAAAB observese la tabla F para dicho texto y tiene el mismo coste que en cualquier otra situacion intermedia entre dichos extremos i 0 1 2 3 4 5 6 7P i A A A A A A A BF i 1 0 1 2 3 4 5 6En el caso de que el primer caracter aparezca una unica vez ver tabla aqui debajo examina la A si coincide examina la B en caso de fallo simplemente salta 1 caracter Si el patron existiere finalmente en el texto sera cuando haya oportunidad de examinar el resto de caracteres antes de alcanzar el final del texto i 0 1 2 3 4 5 6 7P i A B B B B B B BF i 1 0 0 0 0 0 0 0Igualmente cuando el primer caracter se repite excepto en el ultimo caracter Recorreria hasta el ultimo que genera el fallo pero igualmente el puntero absoluto del texto solo avanza 1 con cada fallo La diferencia entre uno y otro casos consiste basicamente en donde se localiza el puntero en el patron y en que momento se recorre el resto del patron si al principio o al final del texto Casos peor y mejor para el texto Editar Para el texto el mejor caso se da cuando el patron se localiza en la posicion 0 El peor caso se da cuando el patron se localiza en la ultima posicion Si el texto no se encuentra es igual o ligeramente mejor que el peor caso y es en tal caso dependiente del patron ver seccion anterior y siguiente Casos peor y mejor en la busqueda Editar Considerando conjuntamente el patron y el texto el peor caso se puede dar en funcion de donde y cuantas veces surja la ruptura del patron Cada caracter fallido necesita ser comprobado otra vez con aquel con el que se alinea Esto implica que el peor caso puede llegar a tener un costo de O n O k 2 si bien en la practica estos casos son raros pues no es frecuente que deban realizarse busqueda con patrones o textos cuyos caracteres tengan una alta frecuencia de repticion como los que s emuestran de ejemplo a continuacion Ejemplos Patron tabla F Texto nº de comparaciones precisas en todos los ejemplos el tamano del patron y del texto es igual 6 30 AAAAAZ 1 0 1 2 3 4 AAAAACAAAAACAAAAACAAAAACAAAAAZ 51AAAAAZ 1 0 1 2 3 4 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ 54ABBBBB 1 0 0 0 0 0 ABBBBZABBBBZABBBBZABBBBZABBBBB 35 coste tipico m n Ejemplo EditarUna forma de entender correctamente el funcionamiento del algoritmo es seguir paso a paso un ejemplo resenando en cada punto lo que hace o puede hacer el algoritmo en una situacion dada Se considera para el presente articulo que tanto en los ejemplos como en el pseudocodigo los arrays de caracteres se basan en indice 0 Sean 2 cadenas que se entregan como arrays de caracteres a la funcion donde T es el array de caracteres donde queremos buscar y P el array del patron que se quiere hallar en T usando k como puntero absoluto para los caracteres de T e i como puntero para los caracteres de P Se usa tambien una tabla matriz precalculada de la palabra a buscar F A continuacion se ve una figura que representan las variables consideradas para el ejemplo k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Se muestra la tabla F precalculada para el ejemplo i 0 1 2 3 4 5 6P i A B C D A B DF i 1 0 0 0 0 1 2 k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Comienza el calculo los punteros k e i inicialmente valen 0 Si P i T k i se evalua a continuacion si i tamano de P en cuyo caso se habria encontrado la posicion de la cadena En caso contrario se incrementa i Esto sucede 3 veces hasta que en la 4ª ocasion en P 3 tenemos D y en T k i tenemos un espacio momento en que actualizamos k k k i F i k 3 A continuacion verificamos si F i gt 1 F 3 vale 0 como se da el caso hacemos para i F i Es decir saltamos a la posicion sobre el array P que senala F i que en este caso es 0 por tanto al principio del array P pero ahora el puntero del array T esta en 3 k 3 Por tanto en la siguiente figura avanzamos hasta la posicion absoluta de T actual 3 k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Volvemos al principio del bucle cada vez que se vuelve a este punto se comprueba si k alcanzo el tamano de T y de nuevo verificamos si P i T k i Vemos que en el punto actual en P hay un espacio por lo que nuevamente se actualiza k k k i F i k 4 porque F i 1 Ahora entonces se hace i 0 aunque antes tambien era 0 Como volvemos al inicio del bucle se da por comprobado si el bucle finaliza actualizamos la figura con el puntero en k k 4 k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Ahora vemos que varias veces se cumple que P i T k i pero no tantas que se alcance el tamano de P y con cada coincidencia i se ha incrementado por lo que ahora i 6 pero se ha incrementado justo despues de verificar si i tamano P luego devolver k si no habriamos alcanzado la solucion Al analizar de nuevo al inicio del bucle la posicion 6 falla ya que P 6 D y T 4 6 en este punto toca actualizar k y hacemosk k i F i k 4 6 F 6 en este punto F 6 vale 2 por lo que finalmente damos un salto k 4 6 2 8 Y actualizamos i si F 6 gt 1 luego i F i es decir no solo acanzamos el puntero de T sino que ademas avanzamos el puntero de P a 2 porque precisamente en la posicion 8 hay una coincidencia AB tal como comienza la cadena del array w es justo e este punto donde hemos visto el salto y la eficacia de la tabla F Actualizamos la figura damos por verificado la comprobacion del inicio del bucle k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Una nueva vez volvemos comprobar si P i T k i P 2 T 8 2 es decir C como falla toca actualizar el puntero de T k k i F i k 8 2 0 10 y actualizamos i si F i gt 1 luego i F i i 0 Actualizamos a la siguiente figura como cada vez que se actualiza k el puntero absoluto de T k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Con la siguiente comparacion tambien falla ya que A lt gt y nuevamente debe actualizarse el puntero k con su salto de tabla si procede k k i F i k 10 0 1 11 y tambien actualizamos i como esta vez F i 1 entonces volvemos al principio de P es decir i 0 Actualizamos la figura a la nueva posicion que apunta el puntero de T k 11 k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 En esta ocasion de nuevo varias veces se cumplira que P i T k i hasta que se llega a la posicion de i 6 que sucede que P 6 D que esdistinto de T 11 6 C por lo tanto es necesario una nueva bifurcacion hacia la actualizacion de k k k i F i k 11 6 2 15 De nuevo entra en juego el salto de la tabla puesto que F i 2 toca actualizar i si F i gt 1 luego i F i por tanto i 2 Actualizamos una vez mas la figura hasta avanzar hasta k 15 k 01234567890123456789012 T ABC ABCDAB ABCDABCDABDE P ABCDABD i 0123456 F 1000012 Como i 2 porque conseguimos avanzar hasta el indice 6 que en la tabla vale 2 porque antes de la D final hay 2 posiciones coincidentes consecutivamente entonces ahora volvemos a hacer las comprobaciones pero ahora en 15 2 que era Si P i T k i luego si i tamano de w luego devolver k Esta parte se cumple hasta finalmente encontrar por completo la cadena antes de que i pueda ser aumentado a 7 en la parte que sigue a la condicion anterior i i 1 El algoritmo Knuth Morris Pratt para este ejemplo realiza 26 comparaciones hasta encontrar la solucion en la posicion 15 Es necesario recordar que al estar basado en array el indice es 0 frente a la habitual forma de contar caracteres desde la posicion 1 aunque por otra parte en las figuras se ve como los punteros de k e i empiezan en 0 Vease tambien EditarAlgoritmo de busqueda de cadenas Boyer Moore Algoritmo Karp RabinReferencias Editar Algorithms de R Sedgewick Editorial Addison Wesley Publishing Company USA 1983 pagina 242 ISBN 0 201 06672 6 Algorithms R Sedgewick Ed Addison Wesley 1983 pagina 244 y siguientes ISBN 0 201 06672 6 Data Structures amp their Algorithms H R Lewis y L Deneberg Ed Harper Collins 1991 pagina 157 y siguientes ISBN 0 673 39736 X Datos Q45285Obtenido de https es wikipedia org w index php title Algoritmo Knuth Morris Pratt amp oldid 133692421, wikipedia, wiki, leyendo, leer, libro, biblioteca,

español

, española, descargar, gratis, descargar gratis, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, imagen, música, canción, película, libro, juego, juegos