|
| 1 | +# Subsecuencia común más larga |
| 2 | + |
| 3 | +#### Declaración de problema |
| 4 | + |
| 5 | +Dadas dos cadenas `S` y `T`, busque la longitud de la subsecuencia común más larga (<b>LCS</b>). |
| 6 | + |
| 7 | +#### Enfoque |
| 8 | + |
| 9 | +Que el `dp[i][j]` sea la longitud de la subsecuencia común más larga de los prefijos `S[1..i]` y `T[1..j]`. Nuestra respuesta (la longitud de LCS) es `dp[| S|] [| T|]`, ya que el prefijo de la longitud de la cadena es la propia cadena. |
| 10 | + |
| 11 | +Tanto `dp[0][i]` como `dp[i][0]` son `0` para cualquier `i`, desde el LCS de prefijo vacío y cualquier otra cosa es una cadena vacía. |
| 12 | + |
| 13 | +Ahora, vamos a tratar de calcular `dp[i][j]` para cualquier `i`, `j`. Digamos `S[1..i] = *A` y `T[1..j] = *B` donde `*` significa cualquier secuencia de letras (podría ser diferente para `S` y `T`), `A` significa cualquier letra y `B` significa cualquier letra diferente de `A`. Dado que `A != B`, nuestro LCS no incluye `A` o `B` como último carácter. Así que podríamos tratar de tirar a la distancia el personaje `A` o `B`. Si lanzamos `A`, nuestra longitud LCS será `dp[i - 1][j]` (ya que tenemos prefijos `S[1..i - 1]` y `T[1..j]`). Si intentamos lanzar el carácter `B`, tendremos prefijos `S[1..i]` y `T[1..j - 1]`, por lo que la longitud de LCS será `dp[i][j - 1]`. Mientras buscamos la subsecuencia común más <b>larga<b>, elegiremos <b>el valor máximo</b> de `dp[i][j - 1]` y `dp[i - 1] [j]`. |
| 14 | + |
| 15 | +¿Pero qué pasa si `S[1..i] = *A` y `T[1..j] = *A`? Podríamos decir que el LCS de nuestros prefijos es LCS de prefijos `S[1..i - 1]` y `T[1..j - 1]` <b>más</b> la letra `A`. Así que `dp[i][j] = dp[i - 1][j - 1] + 1` es igual a `S[i] = T[j]`. |
| 16 | + |
| 17 | +Pudimos ver que podemos llenar nuestra tabla `dp` fila por fila, columna por columna. Así que nuestro algoritmo será como: |
| 18 | + |
| 19 | +- Digamos que tenemos cuerdas `S` de la longitud N y `T` de la longitud M (numeradas a partir de 1). Vamos a crear la tabla `dp` de tamaño `(N + 1) x (M + 1)` numerada a partir de 0. |
| 20 | +- Vamos a llenar la 0ª fila y la 0ª columna de `dp` con 0. |
| 21 | +- A continuación, seguimos el algoritmo: |
| 22 | + |
| 23 | +```python |
| 24 | +for i in range(1..N): |
| 25 | + for j in range(1..M): |
| 26 | + if(S[i] == T[j]) |
| 27 | + dp[i][j] = dp[i - 1][j - 1] + 1 |
| 28 | + else |
| 29 | + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) |
| 30 | +``` |
| 31 | + |
| 32 | +#### Complejidad horaria |
| 33 | + |
| 34 | +`O(N * M)` - En cualquier caso |
| 35 | + |
| 36 | +#### Complejidad espacial |
| 37 | + |
| 38 | +`O(N * M)` - implementación simple |
| 39 | +`O(min {N, M})` - implementación de dos capas (como `dp[i][j]` depende sólo de las capas i-th e i-th, coudld almacenamos sólo dos capas). |
| 40 | + |
| 41 | +#### Ejemplo |
| 42 | + |
| 43 | +Digamos que tenemos cuerdas `ABCB` y `BBCB`. Construiremos una mesa de este tipo: |
| 44 | + |
| 45 | +``` |
| 46 | +# # Una B C B |
| 47 | +# 0 0 0 0 0 |
| 48 | +B 0 ? ? ? ? |
| 49 | +B 0 ? ? ? ? |
| 50 | +C 0 ? ? ? |
| 51 | +B 0 ? ? ? ? |
| 52 | +``` |
| 53 | + |
| 54 | +Ahora empezaremos a llenar nuestra mesa desde la 1ª fila. Puesto que `S[1] = A` y `T[1] = B`, `dp[1] [1]` será el valor máximo de `dp[0] [1] = 0` y `dp[1] [0] = 0`. Así que `dp[1] [1] = 0`. Pero ahora `S[2] = B = T[1]`, así que `dp[1] [2] = dp[0] [1] + 1 = 1`. `dp[1] [3]` es `1` desde `A != C` y elegimos `max{dp[1] [2], dp[0] [3]}`. Y `dp[1] [4] = dp[0] [3] + 1 = 1`. |
| 55 | + |
| 56 | +``` |
| 57 | +# # A B C B |
| 58 | +# 0 0 0 0 0 |
| 59 | +B 0 0 1 1 1 |
| 60 | +B 0 ? ? ? ? |
| 61 | +C 0 ? ? ? ? |
| 62 | +B 0 ? ? ? ? |
| 63 | +``` |
| 64 | + |
| 65 | +Ahora vamos a llenar la otra parte de la tabla: |
| 66 | + |
| 67 | +``` |
| 68 | +# # A B C B |
| 69 | +# 0 0 0 0 0 |
| 70 | +B 0 0 1 1 1 |
| 71 | +B 0 0 1 1 2 |
| 72 | +C 0 0 1 2 2 |
| 73 | +B 0 0 1 2 3 |
| 74 | +``` |
| 75 | + |
| 76 | +Así que la longitud de LCS es `dp[4] [4] = 3`. |
| 77 | + |
| 78 | +#### Enlaces de implementación de código |
| 79 | + |
| 80 | +- [Java](https://github.com/TheAlgorithms/Java/blob/master/Dynamic%20Programming/LongestCommonSubsequence.java) |
| 81 | +- [Python](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/longest_common_subsequence.py) |
| 82 | +- [C++](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/Dynamic%20Programming/Longest%20Common%20Subsequence.cpp) |
| 83 | +- [JavaScript](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/LongestCommonSubsequence.js) |
| 84 | +- [Go](https://github.com/TheAlgorithms/Go/blob/master/dynamicprogramming/longestCommonSubsequence.go) |
| 85 | +- [Rust](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/longest_common_subsequence.rs) |
| 86 | + |
| 87 | +#### Explicación en YouTube |
| 88 | + |
| 89 | +[Explicación en YouTube de Tushar Roy](https://youtu.be/NnD96abizww) |
0 commit comments