Задача о наибольшей общей подпоследовательности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 1: Строка 1:
  
 
+
== ==
== Определения ==
 
 
{{Определение
 
{{Определение
 
|definition=
 
|definition=
Строка 11: Строка 10:
 
Последовательность <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=
 
|definition=
 
Найти '''наибольшую общую подпоследовательность''' (англ. ''longest common subsequence, <tex>LCS</tex>''), которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух).
 
Найти '''наибольшую общую подпоследовательность''' (англ. ''longest common subsequence, <tex>LCS</tex>''), которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух).
 
}}
 
}}
 
 
== Наивное решение ==
 
== Наивное решение ==
 
Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая <tex>LCS</tex> гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.
 
Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая <tex>LCS</tex> гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.
Строка 22: Строка 19:
 
== Динамическое программирование ==
 
== Динамическое программирование ==
  
Данная задача решается с использованием принципа оптимальности на префиксе.
+
Данная задача решается с использованием [[Принцип_оптимальности_на_префиксе | принципа оптимальности на префиксе]].
  
 
=== Доказательство оптимальности ===
 
=== Доказательство оптимальности ===
Строка 29: Строка 26:
 
Пусть имеются последовательности <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 = \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> — <tex>LCS</tex> <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(X_{m - 1},Y_{n - 1})</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 x_m </tex> следует, что <tex> Z </tex> — <tex>LCS(X_{m - 1},Y)</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>
+
# Если <tex> x_m \neq y_n </tex>, то из <tex> z_k \neq y_n </tex> следует, что <tex> Z </tex> — <tex>LCS(X,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> — <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 = 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(X,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>) — <tex>LCS</tex> <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(X,Y)</tex>.
 
# Аналогично второму случаю.
 
# Аналогично второму случаю.
 
}}
 
}}
Строка 54: Строка 51:
  
 
=== Построение подпоследовательности ===
 
=== Построение подпоследовательности ===
Для каждой пары элементов помимо длины <tex>LCS</tex> соответствующих префиксов хранятся и номера последних элементов, участвующих в этой LCS.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность.
+
Для каждой пары элементов помимо длины <tex>LCS</tex> соответствующих префиксов хранятся и номера последних элементов, участвующих в этой <tex>LCS</tex>.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность.
  
 
=== Псевдокод ===
 
=== Псевдокод ===
Строка 60: Строка 57:
  
 
   ''<font color="green">// подсчёт таблиц</font>''
 
   ''<font color="green">// подсчёт таблиц</font>''
  LCS(x, y)
+
'''int''' LCS(x, y : '''int'''):
 
     m = length(x)
 
     m = length(x)
 
     n = length(y)
 
     n = length(y)
     '''for''' i = 1 to m
+
     '''for''' i = 1 '''to''' m
 
       lcs[i][0] = 0
 
       lcs[i][0] = 0
     '''for''' j = 0 to n
+
     '''for''' j = 0 '''to''' n
 
       lcs[0][j] = 0
 
       lcs[0][j] = 0
     '''for''' i = 1 to m
+
     '''for''' i = 1 '''to''' m
       '''for''' j = 1 to n
+
       '''for''' j = 1 '''to''' n
 
         '''if''' x[i] == y[j]
 
         '''if''' x[i] == y[j]
 
           lcs[i][j] = lcs[i - 1][j - 1] + 1
 
           lcs[i][j] = lcs[i - 1][j - 1] + 1
Строка 80: Строка 77:
 
             prev[i][j] = pair(i, j - 1)
 
             prev[i][j] = pair(i, j - 1)
 
    
 
    
   ''<font color="green">// вывод LCS, вызывается как printLCS(prev, x, m, n)</font>''
+
   ''<font color="green">// вывод LCS, вызывается как printLCS(m, n)</font>''
   printLCS(prev, x, i, j)
+
   '''int''' printLCS(i, j: '''int'''):
     '''if''' i == 0 or j == 0  ''<font color="green">// пришли к началу LCS</font>''
+
     '''if''' i == 0 '''or''' j == 0  ''<font color="green">// пришли к началу LCS</font>''
 
       '''return'''
 
       '''return'''
 
     '''if''' prev[i][j] == pair(i - 1, j - 1)  ''<font color="green">// если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент</font>''
 
     '''if''' prev[i][j] == pair(i - 1, j - 1)  ''<font color="green">// если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент</font>''
       printLCS(prev, x, i - 1, j - 1)
+
       printLCS(i - 1, j - 1)
 
       print x[i]
 
       print x[i]
 
     '''else'''
 
     '''else'''
 
       '''if''' prev[i][j] == pair(i - 1, j)
 
       '''if''' prev[i][j] == pair(i - 1, j)
         printLCS(prev, x, i - 1, j)
+
         printLCS(i - 1, j)
 
       '''else'''
 
       '''else'''
         printLCS(prev, x, i, j - 1)
+
         printLCS(i, j - 1)
  
 
== Оптимизация для вычисления только длины <tex>LCS</tex> ==
 
== Оптимизация для вычисления только длины <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> элементов таблицы:
  
   LCS2(x, y)
+
   '''int''' LCS2(x, y: '''int'''):
 
     '''if''' length(x) < length(y)  ''<font color="green">// в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y</font>''
 
     '''if''' length(x) < length(y)  ''<font color="green">// в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y</font>''
 
       swap(x, y)
 
       swap(x, y)
 
     m = length(x)
 
     m = length(x)
 
     n = length(y)
 
     n = length(y)
     '''for''' j = 0 to n
+
     '''for''' j = 0 '''to''' n
 
       lcs[0][j] = 0
 
       lcs[0][j] = 0
 
       lcs[1][j] = 0
 
       lcs[1][j] = 0
     '''for''' i = 1 to m
+
     '''for''' i = 1 '''to''' m
 
       lcs[1][0] = 0
 
       lcs[1][0] = 0
       '''for''' j = 1 to n
+
       '''for''' j = 1 '''to''' n
 
         lcs[0][j] = lcs[1][j]  ''<font color="green">// элемент, который был в a[1][j], теперь в предыдущей строчке</font>''
 
         lcs[0][j] = lcs[1][j]  ''<font color="green">// элемент, который был в a[1][j], теперь в предыдущей строчке</font>''
 
         '''if''' x[i] == y[j]
 
         '''if''' x[i] == y[j]
Строка 119: Строка 116:
 
Также можно заметить, что от <tex> (i - 1) </tex>-ой строчки нужны только элементы с <tex> (j - 1) </tex>-го столбца. В этом случае можно использовать лишь <tex> min(m, n) </tex> элементов таблицы:
 
Также можно заметить, что от <tex> (i - 1) </tex>-ой строчки нужны только элементы с <tex> (j - 1) </tex>-го столбца. В этом случае можно использовать лишь <tex> min(m, n) </tex> элементов таблицы:
  
   LCS3(x, y)
+
   '''int''' LCS3(x, y: '''int'''):
 
     '''if''' length(x) < length(y)  ''<font color="green">// в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y</font>''
 
     '''if''' length(x) < length(y)  ''<font color="green">// в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y</font>''
 
       swap(x, y)
 
       swap(x, y)
 
     m = length(x)
 
     m = length(x)
 
     n = length(y)
 
     n = length(y)
     '''for''' j = 0 to n
+
     '''for''' j = 0 '''to''' n
 
       lcs[j] = 0
 
       lcs[j] = 0
 
     d = 0  ''<font color="green">// d — дополнительная переменная, в ней хранится lcs[i - 1][j - 1]''
 
     d = 0  ''<font color="green">// 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[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]</font>''
 
           ''// в lcs[0], lcs[1], …, lcs[j - 1] хранятся lcs[i][0], lcs[i][1], …, lcs[i][j - 1]</font>''
     '''for''' i = 1 to m
+
     '''for''' i = 1 '''to''' m
       '''for''' j = 1 to n
+
       '''for''' j = 1 '''to''' n
 
         tmp = lcs[j]
 
         tmp = lcs[j]
 
         '''if''' x[i] == y[i]
 
         '''if''' x[i] == y[i]
Строка 142: Строка 139:
 
     ''<font color="green">// ответ — lcs[n]</font>''
 
     ''<font color="green">// ответ — lcs[n]</font>''
  
== Длинна кратчайшей общей суперпоследовательности ==
+
== Длина кратчайшей общей суперпоследовательности ==
Для двух подпоследовательностей <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>X_{m - 1}</tex> и <tex>Y_{n - 1}</tex> длина кратчайшая общей суперпоследовательности равна   
 
<tex>
 
<tex>
 
|SCS(X,Y)| = n + m - |LCS(X,Y)|
 
|SCS(X,Y)| = n + m - |LCS(X,Y)|

Версия 11:49, 10 января 2015

Определение:
Последовательность [math] Z = \left \langle z_1, z_2, \dots, z_k \right \rangle [/math] является подпоследовательностью (англ. subsequence) последовательности [math] X = \left \langle x_1, x_2, \dots, x_m \right \rangle [/math], если существует строго возрастающая последовательность [math] \left \langle i_1, i_2, \dots, i_k \right \rangle [/math] индексов [math] X [/math] таких, что для всех [math] j = 1, 2, \dots, k [/math] выполняется соотношение [math] x_{i_j} = z_j [/math].

Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например, [math] Z = \left \langle B, C, D, B \right \rangle [/math] является подпоследовательностью последовательности [math] X = \left \langle A, B, C, B, D, A, B \right \rangle [/math], а соответствующая последовательность индексов имеет вид [math] \left \langle 2, 3, 5, 7 \right \rangle [/math].

Определение:
Последовательность [math] Z [/math] является общей подпоследовательностью (англ. common subsequence) последовательностей [math] X [/math] и [math] Y [/math], если [math] Z [/math] является подпоследовательностью как [math] X [/math], так и [math] Y [/math].


Задача:
Найти наибольшую общую подпоследовательность (англ. longest common subsequence, [math]LCS[/math]), которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух).

Наивное решение

Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая [math]LCS[/math] гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.

Динамическое программирование

Данная задача решается с использованием принципа оптимальности на префиксе.

Доказательство оптимальности

Теорема:
Пусть имеются последовательности [math] X = \left \langle x_1, x_2, \dots, x_m \right \rangle [/math] и [math] Y = \left \langle y_1, y_2, \dots, y_n \right \rangle [/math], а [math] Z = \left \langle z_1, z_2, \dots, z_k \right \rangle [/math] — их [math]LCS[/math].
  1. Если [math] x_m = y_n [/math], то [math] z_k = x_m = y_n [/math] и [math] Z_{k - 1} [/math][math]LCS(X_{m - 1},Y_{n - 1})[/math]
  2. Если [math] x_m \neq y_n [/math], то из [math] z_k \neq x_m [/math] следует, что [math] Z [/math][math]LCS(X_{m - 1},Y)[/math]
  3. Если [math] x_m \neq y_n [/math], то из [math] z_k \neq y_n [/math] следует, что [math] Z [/math][math]LCS(X,Y_{n - 1})[/math]
Доказательство:
[math]\triangleright[/math]
  1. Если бы выполнялось [math] z_k \neq x_m [/math], то к [math] Z [/math] можно было бы добавить элемент [math] x_m = y_n [/math], и тогда получилась бы общая подпоследовательность длины [math] k + 1 [/math], что противоречит предположению, что [math] Z [/math][math]LCS[/math]. Значит, выполняется [math] z_k = x_m = y_n [/math]. Значит, [math] Z_{k - 1} [/math] — общая подпоследовательность [math] X_{m - 1} [/math] и [math] Y_{n - 1} [/math]. Докажем от противного, что [math] Z_{k - 1} [/math][math]LCS[/math]: тогда есть общая подпоследовательность [math] W [/math], длина которой больше [math] k - 1 [/math]. Добавив к [math] W [/math] элемент [math] x_m = y_n [/math], получим [math]LCS(X,Y)[/math], длина которой больше [math] k [/math] (т.е. больше длины [math] Z [/math]), что приводит к противоречию.
  2. Если [math] z_k \neq x_m [/math], то [math] Z [/math] — общая подпоследовательность [math] X_{m - 1} [/math] и [math] Y [/math]. Пусть существует их общая подпоследовательность [math] W [/math], длина которой превышает [math] k [/math]. Она также является общей подпоследовательностью [math] X [/math] и [math] Y [/math], что противоречит предположению о том, что [math] Z [/math] (длины [math] k [/math]) — [math]LCS(X,Y)[/math].
  3. Аналогично второму случаю.
[math]\triangleleft[/math]

Решение

Обозначим как [math] lcs[i][j] [/math] [math]LCS[/math] префиксов данных последовательностей, заканчивающихся в элементах с номерами [math] i [/math] и [math] j [/math] соответственно. Получается следующее рекуррентное соотношение:

[math] lcs[i][j] = \begin{cases} 0, & i = 0\text{ or }j = 0 \\ lcs[i - 1][j - 1] + 1, & x[i] = y[j] \\ max(lcs[i][j - 1], lcs[i - 1][j]), & x[i] \neq y[j] \end{cases} [/math]

Очевидно, что сложность алгоритма составит [math] O(mn) [/math], где [math] m [/math] и [math] n [/math] — длины последовательностей.

Построение подпоследовательности

Для каждой пары элементов помимо длины [math]LCS[/math] соответствующих префиксов хранятся и номера последних элементов, участвующих в этой [math]LCS[/math].Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность.

Псевдокод

[math] x [/math], [math] y [/math] — данные последовательности; [math] lcs[i][j] [/math][math]LCS[/math] для префикса длины [math] i [/math] последовательности [math] x [/math] и префикса длины [math] j [/math] последовательности [math] y [/math]; [math] prev[i][j] [/math] — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении [math] lcs[i][j] [/math].

  // подсчёт таблиц
int LCS(x, y : int):
   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(m, n)
 int printLCS(i, j: int):
   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(i - 1, j - 1)
     print x[i]
   else
     if prev[i][j] == pair(i - 1, j)
       printLCS(i - 1, j)
     else
       printLCS(i, j - 1)

Оптимизация для вычисления только длины [math]LCS[/math]

Заметим, что для вычисления [math] lcs[i][j] [/math] нужны только [math] i [/math]-ая и [math] (i-1) [/math]-ая строчки матрицы [math] lcs [/math]. Тогда можно использовать лишь [math] 2 \cdot min(m, n) [/math] элементов таблицы:

 int LCS2(x, y: int):
   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]

Также можно заметить, что от [math] (i - 1) [/math]-ой строчки нужны только элементы с [math] (j - 1) [/math]-го столбца. В этом случае можно использовать лишь [math] min(m, n) [/math] элементов таблицы:

 int LCS3(x, y: int):
   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]

Длина кратчайшей общей суперпоследовательности

Для двух подпоследовательностей [math]X_{m - 1}[/math] и [math]Y_{n - 1}[/math] длина кратчайшая общей суперпоследовательности равна [math] |SCS(X,Y)| = n + m - |LCS(X,Y)| [/math] [1]

См. также

Примечания

Список литературы

  • Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — с. 459. — ISBN 5-8489-0857-4