El problema de la Longest Common Subsequence
El Longest Common Subsequence (LCS) es un problema clásico en informática. Las soluciones para resolver el problema LCS aparecen a menudo en entrevistas de programación y cursos de algoritmos.
¿En qué consiste el problema de la Longest Common Subsequence?
El objetivo es encontrar la subsecuencia común más larga (“Longest Common Subsequence”) de dos secuencias. Una subsecuencia se deriva de una secuencia original. La subsecuencia tiene el mismo orden de elementos, pero algunos pueden haber sido eliminados. Te mostramos este principio con algunos ejemplos:
Secuencia X |
Secuencia Y |
LCS(X, Y) |
---|---|---|
FATHER
|
VATER
|
ATER
|
MOTHER
|
MUTTER
|
MTER
|
DAVID
|
DANIEL
|
DAI
|
Existe una diferencia importante entre la subsecuencia común más larga y la subcadena común más larga (“longest common substring”). Una subcadena, a diferencia de la subsecuencia, no puede contener espacios. Utilizando el ejemplo DAVID
/ DANIEL
, la subsecuencia común más larga sería “DA”, ya que “I” está interrumpida por “V” y “N” respectivamente.
Un ejemplo práctico del problema de la LCS
El problema de la Longest Common Subsequence es importante en todos los ámbitos en los que se trabaje con secuencias derivadas unas de otras. Las soluciones para encontrar la LCS se utilizan, por ejemplo, para examinar textos en busca de similitudes y diferencias. De esta forma puede detectarse el plagio. La conocida utilidad “diff”, que muestra los cambios en archivos de código fuente, también se basa en el problema LCS.
En bioinformática, el problema de la Longest Common Subsequence se utiliza en el análisis de secuencias de ADN. Las mutaciones alteran las bases del ADN en posiciones individuales a lo largo del tiempo. La presencia de una secuencia común más larga entre dos secuencias indica un alto parentesco genético. De este modo, se pueden rastrear los avances genéticos entre especies a lo largo de la evolución.
Los lingüistas utilizan el problema de la Longest Common Subsequence para estudiar la evolución de las lenguas a lo largo de los siglos. Si se encuentran dos palabras de lenguas diferentes que tienen el mismo significado y comparten una secuencia común larga, esto indica un origen común. De esta forma, la evolución histórica puede deducirse a partir de la correspondencia de las letras.
¿Cómo funcionan las soluciones al problema de la Longest Common Subsequence?
El problema LCS puede resolverse, en primer lugar, con un planteamiento “ingenuo” sin ninguna optimización o abordaje especial. Se determinan todas las subsecuencias de las dos secuencias y se encuentra la secuencia más larga que ambas tienen en común. Este enfoque no es del todo eficiente y solo es adecuado para secuencias cortas.
A continuación, te mostramos tres enfoques más eficientes del problema LCS:
- Enfoque recursivo
- Optimización mediante memoización
- Programación dinámica
Todos los enfoques tienen en común la distinción de tres casos con respecto a dos secuencias:
- La última letra es igual
- La última letra no es igual
- La longitud de una de las secuencias es cero
Los enfoques difieren en complejidad temporal (tiempo de ejecución asintótico) y espacial (requisitos de memoria):
Enfoque | Tiempo de ejecución | Memoria requerida |
---|---|---|
Enfoque ingenuo | O(n * n²)
|
O(n)
|
Enfoque recursivo | O(n²)
|
O(1)
|
Optimización por memoización | O(n *m)
|
O(n* m)
|
Programación dinámica | O(n *m)
|
O(n* m)
|
Cada uno de los algoritmos que se presentan a continuación determina la longitud de la subsecuencia común más larga. Puede haber varias subsecuencias concretas de esta longitud que pueden determinarse mediante otros pasos.
Determinar recursivamente la Longest Common Subsequence
Al examinar el problema LCS, resulta evidente que tiene una “subestructura óptima”. Esto significa que el problema puede reducirse a subproblemas. Para encontrar la solución, un enfoque recursivo es idóneo. Te mostramos la implementación del algoritmo en tres lenguajes de programación populares.
Python
Java
- Domina el mercado con nuestra oferta 3x1 en dominios
- Tu dominio protegido con SSL Wildcard gratis
- 1 cuenta de correo electrónico por contrato
C++
Optimización del enfoque recursivo mediante memoización
Un examen más detallado del planteamiento recursivo calcula subsecuencias solapadas. Esta propiedad, denominada “superposición de subproblemas”, es conocida como la secuencia de Fibonacci. También en este caso, las partes recursivas se calculan una y otra vez para llegar a la solución. Para que el proceso sea más eficiente, es muy práctico utilizar la memoización. En otras palabras, almacenamos en caché los subproblemas ya calculados en una matriz bidimensional.
Python
Java
C++
Programación dinámica de la Longest Common Subsequence
La programación dinámica es una técnica no recursiva para resolver problemas de optimización dividiéndolos en subproblemas más pequeños y resolviéndolos de abajo arriba. La programación dinámica se aplica, entre otras cosas, como método de solución para algoritmos de pathfinding. El problema de la Longest Common Subsequence también puede resolverse mediante programación dinámica utilizando una matriz bidimensional.