Задача о наибольшей общей подпоследовательности — различия между версиями
Shersh (обсуждение | вклад) м (→Наивная идея решения) |
Андрей (обсуждение | вклад) |
||
| Строка 1: | Строка 1: | ||
| − | + | ||
| − | |||
| − | |||
| − | |||
== Определения == | == Определения == | ||
{{Определение | {{Определение | ||
|definition= | |definition= | ||
| − | Последовательность <tex> Z = \left \langle z_1, z_2, | + | Последовательность <tex> Z = \left \langle z_1, z_2, \dots, z_k \right \rangle </tex> является '''подпоследовательностью''' (англ. ''subsequence'') последовательности <tex> X = \left \langle x_1, x_2, \dots, x_m \right \rangle </tex>, если существует строго возрастающая последовательность <tex> \left \langle i_1, i_2, \dots, i_k \right \rangle </tex> индексов <tex> X </tex> таких, что для всех <tex> j = 1, 2, \dots, 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>. | Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например, <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= | |definition= | ||
| − | Последовательность <tex> Z </tex> является '''общей подпоследовательностью''' (''common subsequence'') последовательностей <tex> X </tex> и <tex> Y </tex>, если <tex> Z </tex> является подпоследовательностью как <tex> X </tex>, так и <tex> Y </tex>. | + | Последовательность <tex> Z </tex> является '''общей подпоследовательностью''' (англ. ''common subsequence'') последовательностей <tex> X </tex> и <tex> Y </tex>, если <tex> Z </tex> является подпоследовательностью как <tex> X </tex>, так и <tex> Y </tex>. |
| + | }} | ||
| + | |||
| + | {{Задача | ||
| + | |definition= | ||
| + | Найти '''наибольшую общую подпоследовательность''' (англ. ''longest common subsequence, <tex>LCS</tex>''), которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух). | ||
}} | }} | ||
| − | |||
| − | |||
== Наивное решение == | == Наивное решение == | ||
| − | Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая LCS гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей. | + | Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая <tex>LCS</tex> гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей. |
== Динамическое программирование == | == Динамическое программирование == | ||
| Строка 27: | Строка 27: | ||
{{ | {{ | ||
Теорема|statement= | Теорема|statement= | ||
| − | Пусть имеются последовательности <tex> X = \left \langle x_1, x_2, | + | Пусть имеются последовательности <tex> X = \left \langle x_1, x_2, \dots, x_m \right \rangle </tex> и <tex> Y = \left \langle y_1, y_2, \dots, y_n \right \rangle </tex>, а <tex> Z = \left \langle z_1, z_2, \dots, z_k \right \rangle </tex> — их <tex>LCS</tex>. |
| − | # Если <tex> x_m = y_n </tex>, то <tex> z_k = x_m = y_n </tex> и <tex> Z_{k - 1} </tex> — LCS <tex> X_{m - 1} </tex> и <tex> Y_{n - 1} </tex> | + | # Если <tex> x_m = y_n </tex>, то <tex> z_k = x_m = y_n </tex> и <tex> Z_{k - 1} </tex> — <tex>LCS</tex> <tex> X_{m - 1} </tex> и <tex> Y_{n - 1} </tex> |
| − | # Если <tex> x_m \neq y_n </tex>, то из <tex> z_k \neq x_m </tex> следует, что <tex> Z </tex> — LCS <tex> X_{m - 1} </tex> и <tex> Y </tex> | + | # Если <tex> x_m \neq y_n </tex>, то из <tex> z_k \neq x_m </tex> следует, что <tex> Z </tex> — <tex>LCS</tex> <tex> X_{m - 1} </tex> и <tex> Y </tex> |
| − | # Если <tex> x_m \neq y_n </tex>, то из <tex> z_k \neq y_n </tex> следует, что <tex> Z </tex> — LCS <tex> X </tex> и <tex> Y_{n - 1} </tex> | + | # Если <tex> x_m \neq y_n </tex>, то из <tex> z_k \neq y_n </tex> следует, что <tex> Z </tex> — <tex>LCS</tex> <tex> X </tex> и <tex> Y_{n - 1} </tex> |
|proof= | |proof= | ||
| − | # Если бы выполнялось <tex> z_k \neq x_m </tex>, то к <tex> Z </tex> можно было бы добавить элемент <tex> x_m = y_n </tex>, и тогда получилась бы общая подпоследовательность длины <tex> k + 1 </tex>, что противоречит предположению, что <tex> Z </tex> — LCS. Значит, выполняется <tex> z_k = x_m = y_n </tex>. Значит, <tex> Z_{k - 1} </tex> — общая подпоследовательность <tex> X_{m - 1} </tex> и <tex> Y_{n - 1} </tex>. Докажем от противного, что <tex> Z_{k - 1} </tex> — LCS: тогда есть общая подпоследовательность <tex> W </tex>, длина которой больше <tex> k - 1 </tex>. Добавив к <tex> W </tex> элемент <tex> x_m = y_n </tex>, получим LCS <tex> X </tex> и <tex> Y </tex>, длина которой больше <tex> k </tex> (т.е. больше длины <tex> Z </tex>), что приводит к противоречию. | + | # Если бы выполнялось <tex> z_k \neq x_m </tex>, то к <tex> Z </tex> можно было бы добавить элемент <tex> x_m = y_n </tex>, и тогда получилась бы общая подпоследовательность длины <tex> k + 1 </tex>, что противоречит предположению, что <tex> Z </tex> — <tex>LCS</tex>. Значит, выполняется <tex> z_k = x_m = y_n </tex>. Значит, <tex> Z_{k - 1} </tex> — общая подпоследовательность <tex> X_{m - 1} </tex> и <tex> Y_{n - 1} </tex>. Докажем от противного, что <tex> Z_{k - 1} </tex> — <tex>LCS</tex>: тогда есть общая подпоследовательность <tex> W </tex>, длина которой больше <tex> k - 1 </tex>. Добавив к <tex> W </tex> элемент <tex> x_m = y_n </tex>, получим <tex>LCS</tex> <tex> X </tex> и <tex> Y </tex>, длина которой больше <tex> k </tex> (т.е. больше длины <tex> Z </tex>), что приводит к противоречию. |
| − | # Если <tex> z_k \neq x_m </tex>, то <tex> Z </tex> — общая подпоследовательность <tex> X_{m - 1} </tex> и <tex> Y </tex>. Пусть существует их общая подпоследовательность <tex> W </tex>, длина которой превышает <tex> k </tex>. Она также является общей подпоследовательностью <tex> X </tex> и <tex> Y </tex>, что противоречит предположению о том, что <tex> Z </tex> (длины <tex> k </tex>) — LCS <tex> X </tex> и <tex> Y </tex>. | + | # Если <tex> z_k \neq x_m </tex>, то <tex> Z </tex> — общая подпоследовательность <tex> X_{m - 1} </tex> и <tex> Y </tex>. Пусть существует их общая подпоследовательность <tex> W </tex>, длина которой превышает <tex> k </tex>. Она также является общей подпоследовательностью <tex> X </tex> и <tex> Y </tex>, что противоречит предположению о том, что <tex> Z </tex> (длины <tex> k </tex>) — <tex>LCS</tex> <tex> X </tex> и <tex> Y </tex>. |
# Аналогично второму случаю. | # Аналогично второму случаю. | ||
}} | }} | ||
=== Решение === | === Решение === | ||
| − | Обозначим как <tex> lcs[i][j] </tex> LCS префиксов данных последовательностей, заканчивающихся в элементах с номерами <tex> i </tex> и <tex> j </tex> соответственно. Получается следующее рекуррентное соотношение: | + | Обозначим как <tex> lcs[i][j] </tex> <tex>LCS</tex> префиксов данных последовательностей, заканчивающихся в элементах с номерами <tex> i </tex> и <tex> j </tex> соответственно. Получается следующее рекуррентное соотношение: |
<tex> | <tex> | ||
| Строка 54: | Строка 54: | ||
=== Построение подпоследовательности === | === Построение подпоследовательности === | ||
| − | Для каждой пары элементов помимо длины LCS соответствующих префиксов хранятся и номера последних элементов, участвующих в этой LCS.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность. | + | Для каждой пары элементов помимо длины <tex>LCS</tex> соответствующих префиксов хранятся и номера последних элементов, участвующих в этой LCS.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность. |
=== Псевдокод === | === Псевдокод === | ||
| − | <tex> x </tex>, <tex> y </tex> — данные последовательности; <tex> lcs[i][j] </tex> — LCS для префикса длины <tex> i </tex> последовательности <tex> x </tex> и префикса длины <tex> j </tex> последовательности <tex> y </tex>; <tex> prev[i][j] </tex> — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении <tex> lcs[i][j] </tex>. | + | <tex> x </tex>, <tex> y </tex> — данные последовательности; <tex> lcs[i][j] </tex> — <tex>LCS</tex> для префикса длины <tex> i </tex> последовательности <tex> x </tex> и префикса длины <tex> j </tex> последовательности <tex> y </tex>; <tex> prev[i][j] </tex> — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении <tex> lcs[i][j] </tex>. |
''<font color="green">// подсчёт таблиц</font>'' | ''<font color="green">// подсчёт таблиц</font>'' | ||
| Строка 93: | Строка 93: | ||
printLCS(prev, x, i, j - 1) | printLCS(prev, x, i, j - 1) | ||
| − | == Оптимизация для вычисления только длины LCS == | + | == Оптимизация для вычисления только длины <tex>LCS</tex> == |
Заметим, что для вычисления <tex> lcs[i][j] </tex> нужны только <tex> i </tex>-ая и <tex> (i-1) </tex>-ая строчки матрицы <tex> lcs </tex>. Тогда можно использовать лишь <tex> 2 \cdot min(m, n) </tex> элементов таблицы: | Заметим, что для вычисления <tex> lcs[i][j] </tex> нужны только <tex> i </tex>-ая и <tex> (i-1) </tex>-ая строчки матрицы <tex> lcs </tex>. Тогда можно использовать лишь <tex> 2 \cdot min(m, n) </tex> элементов таблицы: | ||
| Строка 142: | Строка 142: | ||
''<font color="green">// ответ — lcs[n]</font>'' | ''<font color="green">// ответ — lcs[n]</font>'' | ||
| − | == Длинна кратчайшей общей | + | == Длинна кратчайшей общей суперпоследовательности == |
| − | Для двух подпоследовательностей <tex> X = \left \langle x_1, x_2, | + | Для двух подпоследовательностей <tex> X = \left \langle x_1, x_2, \dots, x_m \right \rangle </tex> и <tex> Y = \left \langle y_1, y_2, \dots, y_n \right \rangle </tex> длинна кратчайшая общей суперпоследовательности равна |
<tex> | <tex> | ||
|SCS(X,Y)| = n + m - |LCS(X,Y)| | |SCS(X,Y)| = n + m - |LCS(X,Y)| | ||
</tex> | </tex> | ||
<ref>[http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Relation_to_other_problems Wikipedia {{---}} Longest common subsequence problem]</ref> | <ref>[http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Relation_to_other_problems Wikipedia {{---}} Longest common subsequence problem]</ref> | ||
| − | |||
== См. также == | == См. также == | ||
| Строка 154: | Строка 153: | ||
*[[Наибольшая общая возрастающая подпоследовательность]] | *[[Наибольшая общая возрастающая подпоследовательность]] | ||
*[[Задача о наибольшей общей палиндромной подпоследовательности]] | *[[Задача о наибольшей общей палиндромной подпоследовательности]] | ||
| − | |||
== Примечания == | == Примечания == | ||
<references /> | <references /> | ||
| − | |||
| − | |||
== Список литературы == | == Список литературы == | ||
Версия 01:15, 10 января 2015
Содержание
Определения
| Определение: |
| Последовательность является подпоследовательностью (англ. subsequence) последовательности , если существует строго возрастающая последовательность индексов таких, что для всех выполняется соотношение . |
Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например, является подпоследовательностью последовательности , а соответствующая последовательность индексов имеет вид .
| Определение: |
| Последовательность является общей подпоследовательностью (англ. common subsequence) последовательностей и , если является подпоследовательностью как , так и . |
| Задача: |
| Найти наибольшую общую подпоследовательность (англ. longest common subsequence, ), которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух). |
Наивное решение
Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.
Динамическое программирование
Данная задача решается с использованием принципа оптимальности на префиксе.
Доказательство оптимальности
| Теорема: |
Пусть имеются последовательности и , а — их .
|
| Доказательство: |
|
Решение
Обозначим как префиксов данных последовательностей, заканчивающихся в элементах с номерами и соответственно. Получается следующее рекуррентное соотношение:
Очевидно, что сложность алгоритма составит , где и — длины последовательностей.
Построение подпоследовательности
Для каждой пары элементов помимо длины соответствующих префиксов хранятся и номера последних элементов, участвующих в этой LCS.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность.
Псевдокод
, — данные последовательности; — для префикса длины последовательности и префикса длины последовательности ; — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении .
// подсчёт таблиц
LCS(x, y)
m = length(x)
n = length(y)
for i = 1 to m
lcs[i][0] = 0
for j = 0 to n
lcs[0][j] = 0
for i = 1 to m
for j = 1 to n
if x[i] == y[j]
lcs[i][j] = lcs[i - 1][j - 1] + 1
prev[i][j] = pair(i - 1, j - 1)
else
if lcs[i - 1][j] >= lcs[i][j - 1]
lcs[i][j] = lcs[i - 1][j]
prev[i][j] = pair(i - 1, j)
else
lcs[i][j] = lcs[i][j - 1]
prev[i][j] = pair(i, j - 1)
// вывод LCS, вызывается как printLCS(prev, x, m, n)
printLCS(prev, x, i, j)
if i == 0 or j == 0 // пришли к началу LCS
return
if prev[i][j] == pair(i - 1, j - 1) // если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент
printLCS(prev, x, i - 1, j - 1)
print x[i]
else
if prev[i][j] == pair(i - 1, j)
printLCS(prev, x, i - 1, j)
else
printLCS(prev, x, i, j - 1)
Оптимизация для вычисления только длины
Заметим, что для вычисления нужны только -ая и -ая строчки матрицы . Тогда можно использовать лишь элементов таблицы:
LCS2(x, y)
if length(x) < length(y) // в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y
swap(x, y)
m = length(x)
n = length(y)
for j = 0 to n
lcs[0][j] = 0
lcs[1][j] = 0
for i = 1 to m
lcs[1][0] = 0
for j = 1 to n
lcs[0][j] = lcs[1][j] // элемент, который был в a[1][j], теперь в предыдущей строчке
if x[i] == y[j]
lcs[1][j] = lcs[0][j - 1] + 1
else
if lcs[0][j] >= lcs[1][j - 1]
lcs[1][j] = lcs[0][j]
else
lcs[1][j] = lcs[1][j - 1]
// ответ — lcs[1][n]
Также можно заметить, что от -ой строчки нужны только элементы с -го столбца. В этом случае можно использовать лишь элементов таблицы:
LCS3(x, y)
if length(x) < length(y) // в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y
swap(x, y)
m = length(x)
n = length(y)
for j = 0 to n
lcs[j] = 0
d = 0 // d — дополнительная переменная, в ней хранится lcs[i - 1][j - 1]
// в lcs[j], lcs[j + 1], …, lcs[n] хранятся lcs[i - 1][j], lcs[i - 1][j + 1], …, lcs[i - 1][n]
// в lcs[0], lcs[1], …, lcs[j - 1] хранятся lcs[i][0], lcs[i][1], …, lcs[i][j - 1]
for i = 1 to m
for j = 1 to n
tmp = lcs[j]
if x[i] == y[i]
lcs[j] = d + 1
else
if lcs[j] >= lcs[j - 1]
lcs[j] = lcs[j] // в lcs[j] и так хранится lcs[i - 1][j]
else
lcs[j] = lcs[j - 1]
d = tmp
// ответ — lcs[n]
Длинна кратчайшей общей суперпоследовательности
Для двух подпоследовательностей и длинна кратчайшая общей суперпоследовательности равна [1]
См. также
- Задача о наибольшей возрастающей подпоследовательности
- Наибольшая общая возрастающая подпоследовательность
- Задача о наибольшей общей палиндромной подпоследовательности
Примечания
Список литературы
- Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4