
Una subsecuencia común más larga ( LCS ) es la subsecuencia más larga común a todas las secuencias de un conjunto (a menudo solo dos secuencias). Se diferencia de la subcadena común más larga : a diferencia de las subcadenas, las subsecuencias no tienen por qué ocupar posiciones consecutivas dentro de las secuencias originales. El problema de calcular las subsecuencias comunes más largas es un problema clásico de la informática . Debido a su complejidad polinómica y a que cuenta con un algoritmo eficiente para resolverlo, se utiliza para comparar datos y fusionar cambios en archivos en programas como la diffutilidad y los sistemas de control de versiones como Git . Tiene aplicaciones similares en lingüística computacional y bioinformática .
Por ejemplo, consideremos las secuencias (ABCD) y (ACBAD). Tienen cinco subsecuencias comunes de longitud 2: (AB), (AC), (AD), (BD) y (CD); dos subsecuencias comunes de longitud 3: (ABD) y (ACD); y ninguna otra subsecuencia común. Por lo tanto, (ABD) y (ACD) son sus subsecuencias comunes más largas.
Complejidad
Para el caso general de un número arbitrario de secuencias de entrada, el problema es NP-difícil . [ 1 ] Cuando el número de secuencias es constante, el problema se puede resolver en tiempo polinomial mediante programación dinámica .
Dadosecuencias de longitudes, una búsqueda ingenua probaría cada uno de lossubsecuencias de la primera secuencia para determinar si también son subsecuencias de las secuencias restantes; cada subsecuencia puede probarse en un tiempo lineal en las longitudes de las secuencias restantes, por lo que el tiempo para este algoritmo sería
Para el caso de dos secuencias de n y m elementos, el tiempo de ejecución del enfoque de programación dinámica es O ( n × m ). [ 2 ] Para un número arbitrario de secuencias de entrada, el enfoque de programación dinámica proporciona una solución en
Existen métodos de menor complejidad, [ 3 ] que a menudo dependen de la longitud del LCS, del tamaño del alfabeto o de ambos.
La LCS no es necesariamente única; en el peor de los casos, el número de subsecuencias comunes es exponencial en las longitudes de las entradas, por lo que la complejidad algorítmica de enumerar todas las subsecuencias comunes debe ser al menos exponencial. [ 4 ]
Solución para dos secuencias
El problema LCS posee una subestructura óptima : puede dividirse en subproblemas más pequeños y sencillos, que a su vez pueden dividirse en subproblemas aún más sencillos, y así sucesivamente, hasta que, finalmente, la solución se vuelve trivial. En particular, LCS presenta subproblemas superpuestos : las soluciones de subproblemas de alto nivel suelen reutilizar las soluciones de subproblemas de nivel inferior. Los problemas con estas dos propiedades son susceptibles de ser abordados mediante enfoques de programación dinámica , en los que las soluciones de los subproblemas se memorizan , es decir, se guardan para su reutilización.
Prefijos
El prefijo S n de S se define como los primeros n caracteres de S. [ 5 ] Por ejemplo, los prefijos de S = ( AGCA) son
- S 0 = ()
- S 1 = (A)
- S 2 = (AG)
- S 3 = (AGC)
- S 4 = (AGCA).
Sea LCS ( X , Y ) una función que calcula la subsecuencia más larga común a X e Y . Dicha función tiene dos propiedades interesantes.
Primera propiedad
LCS ( X ^ A , Y ^ A ) = LCS ( X , Y )^ A , para todas las cadenas X , Y y todos los símbolos A , donde ^ denota la concatenación de cadenas. Esto permite simplificar el cálculo de LCS para dos secuencias que terminan en el mismo símbolo. Por ejemplo, LCS ("BANANA","ATANA") = LCS ("BANAN","ATAN")^"A", Continuando para los símbolos comunes restantes, LCS ("BANANA","ATANA") = LCS ("BAN","AT")^"ANA".
Segunda propiedad
Si A y B son símbolos distintos ( A ≠ B ), entonces LCS (X^A,Y^B) es una de las cadenas de longitud máxima en el conjunto { LCS ( X ^ A , Y ), LCS ( X , Y ^ B ) }, para todas las cadenas X , Y .
Por ejemplo, LCS ("ABCDEFG","BCDGK") es la cadena más larga entre LCS ("ABCDEFG","BCDG") y LCS ("ABCDEF","BCDGK"); si ambas tuvieran la misma longitud, se podría elegir una de ellas arbitrariamente.
Para realizar la propiedad, distinguimos dos casos:
- Si LCS ("ABCDEFG","BCDGK") termina con una "G", entonces la "K" final no puede estar en LCS, por lo tanto LCS ("ABCDEFG","BCDGK") = LCS ("ABCDEFG","BCDG").
- Si LCS ("ABCDEFG","BCDGK") no termina con una "G", entonces la "G" final no puede estar en LCS, por lo tanto LCS ("ABCDEFG","BCDGK") = LCS ("ABCDEF","BCDGK").
Función LCS definida
Sean dos secuencias definidas de la siguiente manera: y. Los prefijos deson; los prefijos deson. Dejarrepresentan el conjunto de la subsecuencia común más larga de prefijosyEste conjunto de secuencias viene dado por lo siguiente.
Para encontrar el LCS dey, comparary. Si son iguales, entonces la secuenciase extiende mediante ese elemento,. Si no son iguales, entonces la más larga entre las dos secuencias,, y, se conserva. (Si tienen la misma longitud, pero no son idénticas, entonces se conservan ambas). El caso base, cuando cualquieraoestá vacío, es la cadena vacía ,.
Ejemplo resuelto
Se encontrará la subsecuencia más larga común a R = (GAC) y C = (AGCAT). Dado que la función LCS utiliza un elemento "cero", es conveniente definir prefijos cero que estén vacíos para estas secuencias: R 0 = ε; y C 0 = ε. Todos los prefijos se colocan en una tabla con C en la primera fila (convirtiéndola en encabezado de columna ) y R en la primera columna (convirtiéndola en encabezado de fila ).
Esta tabla se utiliza para almacenar la secuencia LCS para cada paso del cálculo. La segunda columna y la segunda fila se han rellenado con ε, ya que cuando se compara una secuencia vacía con una no vacía, la subsecuencia común más larga siempre es una secuencia vacía.
LCS ( R 1 , C 1 ) se determina comparando los primeros elementos de cada secuencia. G y A no son iguales, por lo que este LCS obtiene (usando la "segunda propiedad") la más larga de las dos secuencias, LCS ( R 1 , C 0 ) y LCS ( R 0 , C 1 ). Según la tabla, ambas están vacías, por lo que LCS ( R 1 , C 1 ) también está vacía, como se muestra en la tabla siguiente. Las flechas indican que la secuencia proviene tanto de la celda superior, LCS ( R 0 , C 1 ) como de la celda de la izquierda, LCS ( R 1 , C 0 ).
LCS ( R 1 , C 2 ) se determina comparando G y G. Coinciden, por lo que G se agrega a la secuencia superior izquierda, LCS ( R 0 , C 1 ), que es (ε), dando (εG), que es (G).
Para LCS ( R1 , C3 ) , G y C no coinciden. La secuencia anterior está vacía; la de la izquierda contiene un elemento, G. Seleccionando la más larga de estas, LCS ( R1 , C3 ) es (G). La flecha apunta hacia la izquierda, ya que es la más larga de las dos secuencias .
LCS ( R 1 , C 4 ), asimismo, es (G).
LCS ( R 1 , C 5 ), asimismo, es (G).
Para LCS ( R 2 , C 1 ), A se compara con A. Los dos elementos coinciden, por lo que A se agrega a ε, dando como resultado (A).
Para LCS ( R 2 , C 2 ), A y G no coinciden, por lo que se utiliza la más larga de LCS ( R 1 , C 2 ), que es (G), y LCS ( R 2 , C 1 ), que es (A). En este caso, cada una contiene un elemento, por lo que a esta LCS se le dan dos subsecuencias: (A) y (G).
Para LCS ( R 2 , C 3 ), A no coincide con C. LCS ( R 2 , C 2 ) contiene las secuencias (A) y (G); LCS ( R 1 , C 3 ) es (G), que ya está contenida en LCS ( R 2 , C 2 ). El resultado es que LCS ( R 2 , C 3 ) también contiene las dos subsecuencias, (A) y (G).
Para LCS ( R 2 , C 4 ), A coincide con A, que se agrega a la celda superior izquierda, dando como resultado (GA).
Para LCS ( R 2 , C 5 ), A no coincide con T. Comparando las dos secuencias, (GA) y (G), la más larga es (GA), por lo que LCS ( R 2 , C 5 ) es (GA).
Para LCS ( R 3 , C 1 ), C y A no coinciden, por lo que LCS ( R 3 , C 1 ) obtiene la más larga de las dos secuencias, (A).
Para LCS ( R 3 , C 2 ), C y G no coinciden. Tanto LCS ( R 3 , C 1 ) como LCS ( R 2 , C 2 ) tienen un elemento. El resultado es que LCS ( R 3 , C 2 ) contiene las dos subsecuencias, (A) y (G).
Para LCS ( R 3 , C 3 ), C y C coinciden, por lo que C se agrega a LCS ( R 2 , C 2 ), que contiene las dos subsecuencias, (A) y (G), dando como resultado (AC) y (GC).
Para LCS ( R 3 , C 4 ), C y A no coinciden. La combinación de LCS ( R 3 , C 3 ), que contiene (AC) y (GC), y LCS ( R 2 , C 4 ), que contiene (GA), da un total de tres secuencias: (AC), (GC) y (GA).
Finalmente, para LCS ( R 3 , C 5 ), C y T no coinciden. El resultado es que LCS ( R 3 , C 5 ) también contiene las tres secuencias, (AC), (GC) y (GA).
El resultado final es que la última celda contiene todas las subsecuencias más largas comunes a (AGCAT) y (GAC); estas son (AC), (GC) y (GA). La tabla también muestra las subsecuencias comunes más largas para cada par posible de prefijos. Por ejemplo, para (AGC) y (GA), las subsecuencias comunes más largas son (A) y (G).
Enfoque de rastreo
Para calcular la subsecuencia común más larga (LCS) de una fila de la tabla LCS, solo se necesitan las soluciones de la fila actual y la anterior. Sin embargo, para secuencias largas, estas pueden ser numerosas y extensas, lo que requiere mucho espacio de almacenamiento. Se puede ahorrar espacio de almacenamiento guardando no las subsecuencias en sí, sino su longitud y la dirección de las flechas, como se muestra en la tabla siguiente.
Las subsecuencias reales se deducen mediante un procedimiento de "retroceso" que sigue las flechas hacia atrás, comenzando desde la última celda de la tabla. Cuando la longitud disminuye, las secuencias deben haber tenido un elemento común. Son posibles varias rutas cuando se muestran dos flechas en una celda. A continuación se muestra la tabla para dicho análisis, con números coloreados en las celdas donde la longitud está a punto de disminuir. Los números en negrita trazan la secuencia (GA). [ 6 ]
Relación con otros problemas
Para dos cuerdasy, la longitud de la supersecuencia común más corta está relacionada con la longitud de la LCS mediante [ 3 ].
La distancia de edición cuando solo se permiten inserciones y eliminaciones (sin sustitución), o cuando el costo de la sustitución es el doble del costo de una inserción o eliminación, es:
Código para la solución de programación dinámica
Cálculo de la longitud del LCS
La siguiente función toma como entrada las secuencias X[1..m]y Y[1..n], calcula la LCS entre X[1..i]y Y[1..j]para todos 1 ≤ i ≤ my 1 ≤ j ≤ n, y la almacena en C[i,j]. C[m,n]contendrá la longitud de la LCS de Xy Y. [ 7 ]
función LCSLength(X[1..m], Y[1..n]) C = array(0..m, 0..n) para i := 0..m C[i,0] = 0 para j := 0..n C[0,j] = 0 para i := 1..m para j := 1..n si X[i] = Y[j] C[i,j] := C[i-1,j-1] + 1 demás C[i,j] := max(C[i,j-1], C[i-1,j]) devolver C[m,n]
Como alternativa, se podría utilizar la memorización .
Lectura de un LCS
La siguiente función retrocede las elecciones tomadas al calcular la Ctabla. Si los últimos caracteres de los prefijos son iguales, deben estar en un LCS. Si no, comprueba qué dio el LCS más grande de manteneryy toma la misma decisión. Simplemente elige uno si tienen la misma longitud. Llama a la función con i=my j=n.
función backtrack(C[0..m,0..n], X[1..m], Y[1..n], i, j) si i = 0 o j = 0 devolver "" si X[i] = Y[j] devolver backtrack(C, X, Y, i-1, j-1) + X[i] si C[i,j-1] > C[i-1,j] devolver backtrack(C, X, Y, i, j-1) devolver backtrack(C, X, Y, i-1, j)
Leyendo todos los LCS
Si eligeyEsto daría un resultado de igual longitud; lea ambas subsecuencias resultantes. Esta función devuelve este resultado como un conjunto. Tenga en cuenta que esta función no es polinómica, ya que podría bifurcarse en casi cada paso si las cadenas son similares.
función backtrackAll(C[0..m,0..n], X[1..m], Y[1..n], i, j) si i = 0 o j = 0 devolver {""} si X[i] = Y[j] devolver {Z + X[i] para todo Z en backtrackAll(C, X, Y, i-1, j-1)} R := {} si C[i,j-1] ≥ C[i-1,j] R := backtrackAll(C, X, Y, i, j-1) si C[i-1,j] ≥ C[i,j-1] R := R ∪ backtrackAll(C, X, Y, i-1, j) devolver RImprimir la diferencia
Esta función retrocederá a través de la matriz C e imprimirá la diferencia entre las dos secuencias. Tenga en cuenta que obtendrá una respuesta diferente si intercambia ≥y <, con >y ≤a continuación.
función printDiff(C[0..m,0..n], X[1..m], Y[1..n], i, j) si i >= 0 y j >= 0 y X[i] = Y[j] printDiff(C, X, Y, i-1, j-1) imprimir " " + X[i] de lo contrario, si j > 0 y (i = 0 o C[i,j-1] ≥ C[i-1,j]) printDiff(C, X, Y, i, j-1) imprimir "+ " + Y[j] de lo contrario, si i > 0 y (j = 0 o C[i,j-1] < C[i-1,j]) printDiff(C, X, Y, i-1, j) imprimir "- " + X[i] de lo contrario imprime ""
Ejemplo
Dejarser " XMJYAUZ" yser " MZJAWXU". La subsecuencia común más larga entreyes " MJAU". La tabla Cque se muestra a continuación, que es generada por la función LCSLength, muestra las longitudes de las subsecuencias comunes más largas entre prefijos dey. Elfila yLa columna muestra la longitud del LCS entrey.
Los números resaltados muestran la ruta backtrackque seguiría la función desde la esquina inferior derecha hasta la esquina superior izquierda al leer un LCS. Si los símbolos actuales enyson iguales, son parte del LCS, y vamos hacia arriba y hacia la izquierda (mostrado en negrita ). Si no, vamos hacia arriba o hacia la izquierda, dependiendo de qué celda tenga un número mayor. Esto corresponde a tomar el LCS entrey, oy.
Optimización de código
Se pueden realizar varias optimizaciones al algoritmo anterior para acelerarlo en casos reales.
Reducir el conjunto de problemas
La matriz C en el algoritmo ingenuo crece cuadráticamente con la longitud de las secuencias. Para dos secuencias de 100 elementos, se necesitaría una matriz de 10 000 elementos y se requerirían 10 000 comparaciones. En la mayoría de los casos reales, especialmente en las comparaciones y parches de código fuente, los inicios y finales de los archivos rara vez cambian, y casi con seguridad no ambos a la vez. Si solo han cambiado unos pocos elementos en medio de la secuencia, se pueden eliminar el inicio y el final. Esto reduce no solo los requisitos de memoria para la matriz, sino también la cantidad de comparaciones necesarias.
función LCS(X[1..m], Y[1..n]) inicio := 1 m_fin := m n_fin := n recortar los elementos coincidentes al principio mientras inicio ≤ m_fin y inicio ≤ n_fin y X[inicio] = Y[inicio] inicio := inicio + 1 recortar los elementos coincidentes al final mientras inicio ≤ m_fin y inicio ≤ n_fin y X[m_fin] = Y[n_fin] m_end := m_end - 1 n_fin := n_fin - 1 C = array(inicio-1..m_fin, inicio-1..n_fin) Solo se itera sobre los elementos que han cambiado: para i := inicio..m_fin y para j := inicio..n_fin, el algoritmo continúa como antes...
En el mejor de los casos, una secuencia sin cambios, esta optimización eliminaría la necesidad de la matriz C. En el peor de los casos, un cambio en el primer y último elemento de la secuencia, solo se realizan dos comparaciones adicionales.
Reduzca el tiempo de comparación
La mayor parte del tiempo que tarda el algoritmo ingenuo se emplea en realizar comparaciones entre los elementos de las secuencias. Para secuencias de texto, como el código fuente, conviene considerar las líneas como elementos de la secuencia en lugar de caracteres individuales. Esto puede implicar comparaciones de cadenas relativamente largas en cada paso del algoritmo. Se pueden realizar dos optimizaciones que ayudan a reducir el tiempo que consumen estas comparaciones.
Reducir cadenas a hashes
Se puede utilizar una función hash o una suma de verificación para reducir el tamaño de las cadenas en las secuencias. Es decir, para el código fuente donde la línea promedio tiene 60 caracteres o más, el hash o la suma de verificación para esa línea podría tener solo entre 8 y 40 caracteres. Además, la naturaleza aleatoria de los hashes y las sumas de verificación garantizaría que las comparaciones se realicen más rápidamente, ya que las líneas de código fuente rara vez se modifican al principio.
Esta optimización presenta tres inconvenientes principales. En primer lugar, requiere tiempo para precalcular los hashes de las dos secuencias. En segundo lugar, se necesita memoria adicional para las nuevas secuencias hash. Sin embargo, en comparación con el algoritmo ingenuo utilizado, ambos inconvenientes son relativamente mínimos.
El tercer inconveniente son las colisiones . Dado que no se garantiza la unicidad de la suma de verificación o el hash, existe una pequeña probabilidad de que dos elementos diferentes se reduzcan al mismo hash. Si bien esto es improbable en el código fuente, es posible. Por lo tanto, un hash criptográfico sería mucho más adecuado para esta optimización, ya que su entropía será significativamente mayor que la de una simple suma de verificación. Sin embargo, los beneficios podrían no compensar los requisitos de configuración y computación de un hash criptográfico para secuencias cortas.
Reduzca el espacio necesario
Si solo se requiere la longitud del LCS, la matriz se puede reducir a unamatriz, o a unaEl vector, como enfoque de programación dinámica, solo requiere las columnas actual y anterior de la matriz. El algoritmo de Hirschberg permite la construcción de la secuencia óptima en los mismos límites de tiempo cuadrático y espacio lineal. [ 8 ]
Reducir fallos de caché
Chowdhury y Ramachandran idearon un algoritmo de tiempo cuadrático y espacio lineal [ 9 ] [ 10 ] para encontrar la longitud de LCS junto con una secuencia óptima que se ejecuta más rápido que el algoritmo de Hirschberg en la práctica debido a su rendimiento de caché superior. [ 9 ] El algoritmo tiene una complejidad de caché asintóticamente óptima bajo el modelo de caché ideal . [ 11 ] Curiosamente, el algoritmo en sí es ajeno a la caché [ 11 ] lo que significa que no toma ninguna decisión basada en los parámetros de caché (por ejemplo, tamaño de caché y tamaño de línea de caché) de la máquina.
Algoritmos optimizados adicionales
Existen varios algoritmos que se ejecutan más rápido que el enfoque de programación dinámica presentado. Uno de ellos es el algoritmo de Hunt-Szymanski , que normalmente se ejecuta entiempo (para), dóndees el número de coincidencias entre las dos secuencias. [ 12 ] Para problemas con un tamaño de alfabeto limitado, el Método de los Cuatro Rusos puede utilizarse para reducir el tiempo de ejecución del algoritmo de programación dinámica en un factor logarítmico. [ 13 ]
Comportamiento en cadenas aleatorias
A partir de Chvátal y Sankoff (1975) , [ 14 ] varios investigadores han estudiado el comportamiento de la longitud de la subsecuencia común más larga cuando las dos cadenas dadas se extraen aleatoriamente del mismo alfabeto. Cuando el tamaño del alfabeto es constante, la longitud esperada de la LCS es proporcional a la longitud de las dos cadenas, y las constantes de proporcionalidad (que dependen del tamaño del alfabeto) se conocen como las constantes de Chvátal-Sankoff . No se conocen sus valores exactos, pero se han demostrado límites superiores e inferiores para sus valores, [ 15 ] y se sabe que crecen de forma inversamente proporcional a la raíz cuadrada del tamaño del alfabeto. [ 16 ] Se ha demostrado que los modelos matemáticos simplificados del problema de la subsecuencia común más larga están controlados por la distribución de Tracy-Widom . [ 17 ]
Calcular la subsecuencia palíndroma más larga de una cadena.
Durante décadas, se consideró un mito que la subsecuencia palíndroma más larga de una cadena podía calcularse hallando la subsecuencia común más larga entre la cadena y su inversa, utilizando el enfoque clásico de programación dinámica introducido por Wagner y Fischer. Sin embargo, la prueba formal de la corrección de este método no se estableció hasta 2024 por Brodal, Fagerberg y Moldrup Rysgaard. [ 18 ]
Véase también
Referencias
- ↑ David Maier (1978). "La complejidad de algunos problemas sobre subsecuencias y supersecuencias" . J. ACM . 25 (2). ACM Press: 322–336 . doi : 10.1145/322063.322075 . S2CID 16120634 .
- ↑ Wagner, Robert; Fischer, Michael (enero de 1974). "El problema de la corrección de cadena a cadena". Journal of the ACM . 21 (1): 168– 173. CiteSeerX 10.1.1.367.5281 . doi : 10.1145/321796.321811 . S2CID 13381535 .
- 1 2 L. Bergroth, H. Hakonen y T. Raita (7-29 de septiembre de 2000). Un estudio de los algoritmos de subsecuencia común más larga . Actas del Séptimo Simposio Internacional sobre Procesamiento de Cadenas y Recuperación de Información. SPIRE 2000. Curuña, España: IEEE Computer Society. págs. 39-48 . doi : 10.1109/SPIRE.2000.878178 . ISBN 0-7695-0746-8. S2CID 10375334 .
- ↑ Ronald I. Greenberg (2003-08-06). "Límites en el número de subsecuencias comunes más largas". arXiv : cs.DM/0301030 .
- ↑ Xia, Xuhua (2007). Bioinformática y la célula: Enfoques computacionales modernos en genómica, proteómica y transcriptómica . Nueva York: Springer. pág . 24. ISBN 978-0-387-71336-6.
- ↑ Thomas H. Cormen , Charles E. Leiserson , Ronald L. Rivest y Clifford Stein (2001). «15.4». Introducción a los algoritmos (2.ª ed.). MIT Press y McGraw-Hill. págs. 350–355 . ISBN 0-262-53196-8.
{{cite book}}: CS1 maint: varios nombres: lista de autores ( enlace ) - ↑ Cormen, Thomas H.; Leiserson , Charles E .; Rivest, Ronald L .; Stein, Clifford (2009) [1990]. "Programación dinámica". Introducción a los algoritmos (3.ª ed.). MIT Press y McGraw-Hill. pág. 394. ISBN 0-262-03384-4.
- ↑ Hirschberg, DS (1975). "Un algoritmo de espacio lineal para calcular subsecuencias comunes máximas" . Communications of the ACM . 18 (6): 341– 343. doi : 10.1145/360825.360861 . S2CID 207694727 .
- 1 2 Chowdhury, Rezaul; Ramachandran, Vijaya (enero de 2006). "Programación dinámica sin tener en cuenta la caché" . Actas del decimoséptimo simposio anual ACM-SIAM sobre algoritmos discretos - SODA '06 . págs. 591–600 . doi : 10.1145/1109557.1109622 . ISBN 0-89871-605-5. S2CID 9650418 .
- ↑ Chowdhury, Rezaul; Le, Hai-Son; Ramachandran, Vijaya (julio de 2010). "Programación dinámica independiente de la caché para bioinformática". IEEE/ACM Transactions on Computational Biology and Bioinformatics . 7 (3): 495– 510. Bibcode : 2010ITCBB...7..495C . doi : 10.1109/TCBB.2008.94 . PMID 20671320 . S2CID 2532039 .
- 1 2 Frigo, Mateo; Leiserson, Charles E.; Prokop, Harald; Ramachandran, Sridhar (enero de 2012). "Algoritmos ajenos al caché" . Transacciones ACM sobre algoritmos . 8 (1): 1– 22. doi : 10.1145/2071379.2071383 .
- ↑ Apostolico, Alberto; Galil, Zvi (29 de mayo de 1997). Algoritmos de coincidencia de patrones . Oxford University Press. ISBN 978-0-19-535434-8.
- ↑ Masek, William J.; Paterson, Michael S. (1980), "Un algoritmo más rápido para calcular distancias de edición de cadenas", Journal of Computer and System Sciences , 20 (1): 18–31 , doi : 10.1016/0022-0000(80)90002-1 , hdl : 1721.1/148933 , MR 0566639 .
- ↑ Chvátal, Václáv ; Sankoff, David (1975), "Subsecuencias comunes más largas de dos secuencias aleatorias", Journal of Applied Probability , 12 (2): 306–315 , doi : 10.2307/3212444 , JSTOR 3212444 , MR 0405531 , S2CID 250345191 .
- ↑ Lueker, George S. (2009), "Límites mejorados para la longitud promedio de las subsecuencias comunes más largas", Journal of the ACM , 56 (3), A17, doi : 10.1145/1516512.1516519 , MR 2536132 , S2CID 7232681 .
- ↑ Kiwi, Marcos; Loebl, Martin; Matoušek, Jiří (2005), "Longitud esperada de la subsecuencia común más larga para alfabetos grandes", Advances in Mathematics , 197 (2): 480–498 , arXiv : math/0308234 , doi : 10.1016/j.aim.2004.10.012 , MR 2173842 .
- ↑ Majumdar, Satya N.; Nechaev, Sergei (2005), "Resultados asintóticos exactos para el modelo de coincidencia de Bernoulli de alineación de secuencias", Physical Review E , 72 (2): 020901, 4, arXiv : q-bio/0410012 , Bibcode : 2005PhRvE..72b0901M , doi : 10.1103/PhysRevE.72.020901 , MR 2177365 , PMID 16196539 , S2CID 11390762 .
- ^ Brodal, GS; Fagerberg, R.; Rysgaard, CM (2024). Sobre la búsqueda de subsecuencias palindrómicas más largas utilizando las subsecuencias comunes más largas . Schloss Dagstuhl – Leibniz-Zentrum für Informatik. págs. 35:1–35:16. doi : 10.4230/lipics.esa.2024.35 .
Enlaces externos
- Diccionario de algoritmos y estructuras de datos: subsecuencia común más larga
- Una colección de implementaciones de la subsecuencia común más larga en muchos lenguajes de programación.
- Encontrar la subsecuencia común más larga en Python
- Problemas con cadenas de caracteres
- Combinatoria
- Programación dinámica
- Problemas de tiempo polinomial
- problemas NP-completos