Задача о наибольшей общей подпоследовательности — различия между версиями
(→Решение) |
|||
Строка 1: | Строка 1: | ||
− | Задача нахождения '''наибольшей общей подпоследовательности''' ( | + | Задача нахождения '''наибольшей общей подпоследовательности''' (''longest common subsequence, LCS'') — это задача поиска последовательности, которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух). |
+ | == Определения == | ||
+ | {{Определение | ||
+ | |definition= | ||
+ | Последовательность <tex> Z = \left \langle z_1, z_2, ..., z_k \right \rangle </tex> является '''подпоследовательностью''' (subsequence) последовательности <tex> X = \left \langle x_1, x_2, ..., x_m \right \rangle </tex>, если существует строго возрастающая последовательность <tex> \left \langle i_1, i_2, ..., i_k \right \rangle </tex> индексов <tex> X </tex> таких, что для всех <tex> j = 1, 2, ..., k </tex> выполняется соотношение <tex> x_{i_j} = z_j </tex>. | ||
+ | }} | ||
+ | Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например, <tex> Z = \left \langle B, C, D, B \right \rangle </tex> является подпоследовательностью последовательности <tex> X = \left \langle A, B, C, B, D, A, B \right \rangle </tex>, а соответствующая последовательность индексов имеет вид <tex> \left \langle 2, 3, 5, 7 \right \rangle </tex>. | ||
+ | {{Определение | ||
+ | |definition= | ||
+ | Последовательность <tex> Z </tex> является '''общей подпоследовательностью''' (common subsequence) последовательностей <tex> X </tex> и <tex> Y </tex>, если <tex> Z </tex> является подпоследовательностью как <tex> X </tex>, так и <tex> Y </tex>. | ||
+ | }} | ||
== Постановка задачи == | == Постановка задачи == | ||
− | + | Даны две последовательности: <tex> X = \left \langle x_1, x_2, ..., x_m \right \rangle </tex> и <tex> Y = \left \langle y_1, y_2, ..., y_n \right \rangle </tex>. Требуется найти общую подпоследовательность <tex> X </tex> и <tex> Y </tex> максимальной длины. Заметим, что таких подпоследовательностей может быть несколько. | |
− | |||
== Наивная идея решения == | == Наивная идея решения == | ||
− | |||
Переберем все различные подпоследовательности обеих строк и сравним их. Мы гарантированно найдем искомую НОП, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей. | Переберем все различные подпоследовательности обеих строк и сравним их. Мы гарантированно найдем искомую НОП, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей. | ||
== Динамическое программирование == | == Динамическое программирование == | ||
=== Решение === | === Решение === | ||
− | Обозначим как <tex> | + | Обозначим как <tex> a[i][j] </tex> НОП префиксов данных последовательностей, заканчивающихся в элементах с номерами <tex> i </tex> и <tex> j </tex> соответственно. Получаем следующее рекуррентное соотношение: |
− | + | <tex> | |
− | + | a[i][j] = | |
− | Очевидно, что сложность алгоритма составит <tex>O(n^2)</tex>. | + | \begin{cases} |
+ | a[i - 1][j - 1] + 1, & \text{если }x[i] = y[j]\text{ (соответствующие элементы последовательностей равны); } \\ | ||
+ | max(a[i][j - 1], a[i - 1][j]), & \text{если }x[i] \neq y[j]\text{ (соответствующие элементы последовательностей не равны).} | ||
+ | \end{cases} | ||
+ | </tex> | ||
+ | Очевидно, что сложность алгоритма составит <tex> O(n^2) </tex>. | ||
=== Доказательство оптимальности === | === Доказательство оптимальности === | ||
− | Предположим, что некоторое значение <tex> | + | Предположим, что некоторое значение <tex> a[i][j] </tex> посчитано неверно. Однако, в случае различия соответствующих символов, они не могут одновременно участвовать в НОП, а значит ответ действительно равен формуле для случая с различными символами. В случае же равенства, ответ не может быть больше, чем <tex> a[i - 1][j - 1] + 1 </tex>, так как тогда неверно посчитано значение <tex> a[i - 1][j - 1] + 1 </tex>. |
=== Построение подпоследовательности === | === Построение подпоследовательности === |
Версия 00:06, 20 ноября 2011
Задача нахождения наибольшей общей подпоследовательности (longest common subsequence, LCS) — это задача поиска последовательности, которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух).
Содержание
Определения
Определение: |
Последовательность | является подпоследовательностью (subsequence) последовательности , если существует строго возрастающая последовательность индексов таких, что для всех выполняется соотношение .
Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например,
является подпоследовательностью последовательности , а соответствующая последовательность индексов имеет вид .Определение: |
Последовательность | является общей подпоследовательностью (common subsequence) последовательностей и , если является подпоследовательностью как , так и .
Постановка задачи
Даны две последовательности:
и . Требуется найти общую подпоследовательность и максимальной длины. Заметим, что таких подпоследовательностей может быть несколько.Наивная идея решения
Переберем все различные подпоследовательности обеих строк и сравним их. Мы гарантированно найдем искомую НОП, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.
Динамическое программирование
Решение
Обозначим как
НОП префиксов данных последовательностей, заканчивающихся в элементах с номерами и соответственно. Получаем следующее рекуррентное соотношение: Очевидно, что сложность алгоритма составит .Доказательство оптимальности
Предположим, что некоторое значение
посчитано неверно. Однако, в случае различия соответствующих символов, они не могут одновременно участвовать в НОП, а значит ответ действительно равен формуле для случая с различными символами. В случае же равенства, ответ не может быть больше, чем , так как тогда неверно посчитано значение .Построение подпоследовательности
Для каждой пары элементов будем хранить не только длину НОП соответствующих префиксов, но и номера последних элементов, участвующих в этой НОП.Таким образом, посчитав ответ, мы сможем восстановить всю наибольшую общую подпоследовательность.
Пример реализации на Java
public int lcs(int[] s1, int[] s2) { int[][] a = new int[s1.length][s2.length]; //Подсчет значений for (int i = 0; i < s1.length; i++) for (int j = 0; j < s2.length; j++) { if (s1[i] == s2[j]) if ((i == 0) || (j == 0)) { a[i][j] = 1; } else { a[i][j] = a[i - 1][j - 1] + 1; } else { if ((i > 0) && (a[i - 1][j] > a[i][j])) { a[i][j] = a[i - 1][j]; } if ((j > 0) && (a[i][j - 1] > a[i][j])) { a[i][j] = a[i][j - 1]; } } } return a[s1.length - 1][s2.length - 1]; }