Estoy buscando un algoritmo rápido de coincidencia de cadenas k-incompatibilidad. Dada una cadena de patrón P de longitud m, y una cadena de texto T de longitud n, necesito un algoritmo rápido (tiempo lineal) para encontrar todas las posiciones donde P coincida con una subcadena de T con a lo sumo k desajustes. Esto es diferente del problema de k-diferencias (distancia de edición). Una falta de coincidencia implica que la subcadena y el patrón tienen una letra diferente en la mayoría de las k posiciones. Realmente solo necesito k = 1 (como máximo 1 falta de coincidencia), por lo que un algoritmo rápido para el caso específico de k = 1 también será suficiente. El tamaño del alfabeto es 26 (texto en inglés que no distingue entre mayúsculas y minúsculas), por lo que el requisito de espacio no debería crecer demasiado rápido con el tamaño del alfabeto (por ejemplo, el algoritmo FAAST, creo, ocupa un espacio exponencial en el tamaño del alfabeto, y así es adecuado solo para secuencias de proteínas y genes).
Un enfoque basado en programación dinámica tenderá a ser O (mn) en el peor de los casos, lo que será demasiado lento. Creo que hay modificaciones del algoritmo de Boyer-Moore para esto, pero no puedo tener en mis manos esos documentos. No tengo suscripción para acceder a revistas o publicaciones académicas, por lo que cualquier referencia tendrá que ser de dominio público.
Agradecería mucho cualquier puntero, o referencia a documentos disponibles gratuitamente, o el algoritmo en sí para este problema.
Respuestas:
Se pueden utilizar matrices de sufijos para este problema. Contienen las posiciones iniciales de cada sufijo de la cadena ordenada en orden lexicográfico. Aunque pueden construirse ingenuamente en complejidad , existen métodos para construirlos en complejidad Θ ( n ) . Ver por ejemplo esto y esto . Llamemos a este sufijo array SA.O ( n logn ) Θ ( n )
Una vez que se ha construido la matriz de sufijos, necesitamos construir una matriz de prefijo común más largo (LCP) para la matriz de sufijos. La matriz LCP almacena la longitud del prefijo común más largo entre dos prefijos consecutivos en la matriz de sufijos (sufijos lexicográficos consecutivos). Por lo tanto, LCP [i] contiene la longitud del prefijo común más largo entre SA [i] y SA [i + 1]. Esta matriz también se puede construir en tiempo lineal: vea aquí , aquí y aquí para algunas buenas referencias.
Ahora, para calcular la longitud del prefijo más largo común a cualquiera de los dos sufijos en el árbol de sufijos (en lugar de sufijos consecutivos), necesitamos usar alguna estructura de datos RMQ . Se ha mostrado en las referencias anteriores (y se puede ver fácilmente si la matriz se visualiza como un árbol de sufijos), que la longitud del prefijo común más largo entre dos sufijos que tienen posiciones y v ( u < v ) en la matriz de sufijos , se puede obtener como m i n u < = k < = v - 1 L C P [ k ]tu v u < v m i nu < = k < = v - 1L CPAGS[ k ] . Un buen RMQ puede preprocesar la matriz en el tiempo O ( n ) u O ( n log n ) y responder a las consultas de la forma L C P [ u , v ] en el tiempo O ( 1 ) . Vea aquí un breve algoritmo RMQ, y aquí un buen tutorial sobre RMQ y la relación (y reducciones) entre LCA y RMQ. Esto tiene otro buen enfoque alternativo.L CPAGS O ( n ) O ( n logn ) L CPAGS[ u , v ] O ( 1 )
Con esta información, construimos la matriz de sufijos y las matrices asociadas (como se describió anteriormente) para la concatenación de las dos cadenas con un delimitador en el medio (como T # P, donde '#' no aparece en ninguna de las cadenas). Entonces, podemos realizar k coincidencia de cadenas de desajuste utilizando el método "canguro". Esto y esto explican el método canguro en el contexto de los árboles de sufijos, pero también se pueden aplicar directamente a las matrices de sufijos. Para cada índice del texto T , encuentre el L C P del sufijo de T que comienza en i y el sufijo de Pyo T L CPAGS T yo PAGS comenzando en 0. Esto proporciona la ubicación después de la cual se produce la primera falta de coincidencia al hacer coincidir con T [ i ] . Deje que esta longitud sea l 0 . Omita el carácter no coincidente en T y P e intente hacer coincidir las cadenas restantes. Es decir, nuevamente encuentre el L C P de T [ i + l 0 + 1 ] y P [ l 0 + 1 ] . Repita esto hasta obtener k discrepancias, o cualquiera de las cadenas termina. CadaPAGS T[ i ] l0 0 T PAGS L CPAGS T[ i + l0 0+ 1 ] PAGS[ l0 0+ 1 ] k es O ( 1 ) . Hay O ( k ) L C P 's para cada índice i de T , lo que da una complejidad total de O ( n k ) .L CPAGS O ( 1 ) O ( k ) L CPAGS yo T O ( n k )
Utilicé un RMQ más fácil de implementar que daba una complejidad total de u O ( n k + n log nO ( n k + ( n + m ) log( n + m ) ) si m = O ( n ) , pero también se puede hacer en O ( n k ) como se describió anteriormente. Puede haber otros métodos directos para este problema, pero este es un enfoque poderoso y genérico que se puede aplicar a muchos problemas similares.O ( n k + n logn ) m = O ( n ) O ( n k )
fuente
A continuación se muestra un algoritmo esperado de (que se puede extender a otros k , lo que lo haceO (n+m) k ). (Sin embargo, no he hecho los cálculos para demostrar que es así).O (nk+m)
La idea es similar al algoritmo de hash rodante Rabin-Karp para coincidencias exactas de subcadenas.
La idea es separar cada cadena de longitud en bloques de 2 k de m / 2 kmetro 2 k m / 2 k tamaño cada uno y calcular el hash de laminación para cada bloque (dando valores hash) y comparar estas 2 k valores hash contra el uno del patrón.2 k 2 k
Permitimos a lo sumo desajustes en esos valores.k
Si se producen más de desajustes, rechazamos y seguimos adelante. De lo contrario, intentamos confirmar una coincidencia aproximada.k
Espero (advertencia: no lo he intentado yo mismo) esto probablemente será más rápido en la práctica, y quizás más fácil de codificar / mantener, que usar un enfoque basado en un árbol de sufijos.
fuente