<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.158.116&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.158.116&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/217.66.158.116"/>
		<updated>2026-06-11T14:21:41Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=33887</id>
		<title>Задача о наибольшей общей подпоследовательности</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%D0%B9_%D0%BE%D0%B1%D1%89%D0%B5%D0%B9_%D0%BF%D0%BE%D0%B4%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=33887"/>
				<updated>2013-12-03T17:29:39Z</updated>
		
		<summary type="html">&lt;p&gt;217.66.158.116: /* Оптимизация для вычисления только длины НОП */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Задача нахождения '''наибольшей общей подпоследовательности''' (''longest common subsequence, LCS'') — это задача поиска последовательности, которая является самой длинной подпоследовательностью нескольких последовательностей (обычно двух).&lt;br /&gt;
&lt;br /&gt;
== Определения ==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Последовательность &amp;lt;tex&amp;gt; Z = \left \langle z_1, z_2, ..., z_k \right \rangle &amp;lt;/tex&amp;gt; является '''подпоследовательностью''' (subsequence) последовательности &amp;lt;tex&amp;gt; X = \left \langle x_1, x_2, ..., x_m \right \rangle &amp;lt;/tex&amp;gt;, если существует строго возрастающая последовательность &amp;lt;tex&amp;gt; \left \langle i_1, i_2, ..., i_k \right \rangle &amp;lt;/tex&amp;gt; индексов &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; таких, что для всех &amp;lt;tex&amp;gt; j = 1, 2, ..., k &amp;lt;/tex&amp;gt; выполняется соотношение &amp;lt;tex&amp;gt; x_{i_j} = z_j &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
Другими словами, подпоследовательность данной последовательности — это последовательность, из которой удалили ноль или больше элементов. Например, &amp;lt;tex&amp;gt; Z = \left \langle B, C, D, B \right \rangle &amp;lt;/tex&amp;gt; является подпоследовательностью последовательности &amp;lt;tex&amp;gt; X = \left \langle A, B, C, B, D, A, B \right \rangle &amp;lt;/tex&amp;gt;, а соответствующая последовательность индексов имеет вид &amp;lt;tex&amp;gt; \left \langle 2, 3, 5, 7 \right \rangle &amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
Последовательность &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; является '''общей подпоследовательностью''' (common subsequence) последовательностей &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; является подпоследовательностью как &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt;, так и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
Даны две последовательности: &amp;lt;tex&amp;gt; X = \left \langle x_1, x_2, ..., x_m \right \rangle &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y = \left \langle y_1, y_2, ..., y_n \right \rangle &amp;lt;/tex&amp;gt;. Требуется найти общую подпоследовательность &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt; максимальной длины. Заметим, что таких подпоследовательностей может быть несколько.&lt;br /&gt;
&lt;br /&gt;
== Наивная идея решения ==&lt;br /&gt;
Переберем все различные подпоследовательности обеих строк и сравним их. Тогда искомая НОП гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.&lt;br /&gt;
&lt;br /&gt;
== Динамическое программирование ==&lt;br /&gt;
&lt;br /&gt;
Данная задача решается с использованием принципа оптимальности на префиксе.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство оптимальности ===&lt;br /&gt;
{{&lt;br /&gt;
Теорема|statement=&lt;br /&gt;
Пусть имеются последовательности &amp;lt;tex&amp;gt; X = \left \langle x_1, x_2, ..., x_m \right \rangle &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y = \left \langle y_1, y_2, ..., y_n \right \rangle &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; Z = \left \langle z_1, z_2, ..., z_k \right \rangle &amp;lt;/tex&amp;gt; — их НОП.&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;tex&amp;gt; x_m = y_n &amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt; z_k = x_m = y_n &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Z_{k - 1} &amp;lt;/tex&amp;gt; — НОП &amp;lt;tex&amp;gt; X_{m - 1} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y_{n - 1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Если &amp;lt;tex&amp;gt; x_m \neq y_n &amp;lt;/tex&amp;gt;, то из &amp;lt;tex&amp;gt; z_k \neq x_m &amp;lt;/tex&amp;gt; следует, что &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; — НОП &amp;lt;tex&amp;gt; X_{m - 1} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;&lt;br /&gt;
# Если &amp;lt;tex&amp;gt; x_m \neq y_n &amp;lt;/tex&amp;gt;, то из &amp;lt;tex&amp;gt; z_k \neq y_n &amp;lt;/tex&amp;gt; следует, что &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; — НОП &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y_{n - 1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
# Если бы выполнялось &amp;lt;tex&amp;gt; z_k \neq x_m &amp;lt;/tex&amp;gt;, то к &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; можно было бы добавить элемент &amp;lt;tex&amp;gt; x_m = y_n &amp;lt;/tex&amp;gt;, и тогда получилась бы общая подпоследовательность длины &amp;lt;tex&amp;gt; k + 1 &amp;lt;/tex&amp;gt;, что противоречит предположению, что &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; — НОП. Значит, выполняется &amp;lt;tex&amp;gt; z_k = x_m = y_n &amp;lt;/tex&amp;gt;. Значит, &amp;lt;tex&amp;gt; Z_{k - 1} &amp;lt;/tex&amp;gt; — общая подпоследовательность &amp;lt;tex&amp;gt; X_{m - 1} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y_{n - 1} &amp;lt;/tex&amp;gt;. Докажем от противного, что &amp;lt;tex&amp;gt; Z_{k - 1} &amp;lt;/tex&amp;gt; — НОП: тогда есть общая подпоследовательность &amp;lt;tex&amp;gt; W &amp;lt;/tex&amp;gt;, длина которой больше &amp;lt;tex&amp;gt; k - 1 &amp;lt;/tex&amp;gt;. Добавив к &amp;lt;tex&amp;gt; W &amp;lt;/tex&amp;gt; элемент &amp;lt;tex&amp;gt; x_m = y_n &amp;lt;/tex&amp;gt;, получим НОП &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;, длина которой больше &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; (т.е. больше длины &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt;), что приводит к противоречию.&lt;br /&gt;
# Если &amp;lt;tex&amp;gt; z_k \neq x_m &amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; — общая подпоследовательность &amp;lt;tex&amp;gt; X_{m - 1} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;. Пусть существует их общая подпоследовательность &amp;lt;tex&amp;gt; W &amp;lt;/tex&amp;gt;, длина которой превышает &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt;. Она также является общей подпоследовательностью &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;, что противоречит предположению о том, что &amp;lt;tex&amp;gt; Z &amp;lt;/tex&amp;gt; (длины &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt;) — НОП &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; Y &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Аналогично второму случаю.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Решение ===&lt;br /&gt;
Обозначим как &amp;lt;tex&amp;gt; lcs[i][j] &amp;lt;/tex&amp;gt; НОП префиксов данных последовательностей, заканчивающихся в элементах с номерами &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; соответственно. Получается следующее рекуррентное соотношение:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
lcs[i][j] =&lt;br /&gt;
\begin{cases}&lt;br /&gt;
 0, &amp;amp; i = 0\text{ or }j = 0 \\&lt;br /&gt;
 lcs[i - 1][j - 1] + 1, &amp;amp; x[i] = y[j] \\&lt;br /&gt;
 max(lcs[i][j - 1], lcs[i - 1][j]), &amp;amp; x[i] \neq y[j]&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Очевидно, что сложность алгоритма составит &amp;lt;tex&amp;gt; O(mn) &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; — длины последовательностей.&lt;br /&gt;
&lt;br /&gt;
=== Построение подпоследовательности ===&lt;br /&gt;
Для каждой пары элементов помимо длины НОП соответствующих префиксов хранятся и номера последних элементов, участвующих в этой НОП.Таким образом, посчитав ответ, можно восстановить всю наибольшую общую подпоследовательность.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt; — данные последовательности; &amp;lt;tex&amp;gt; lcs[i][j] &amp;lt;/tex&amp;gt; — НОП для префикса длины &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; последовательности &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; и префикса длины &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; последовательности &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;; &amp;lt;tex&amp;gt; prev[i][j] &amp;lt;/tex&amp;gt; — пара индексов элемента таблицы, соответствующего оптимальному решению вспомогательной задачи, выбранной при вычислении &amp;lt;tex&amp;gt; lcs[i][j] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  // подсчёт таблиц&lt;br /&gt;
  LCS(x, y)&lt;br /&gt;
    m = length(x)&lt;br /&gt;
    n = length(y)&lt;br /&gt;
    for i = 1 to m&lt;br /&gt;
      lcs[i][0] = 0&lt;br /&gt;
    for j = 0 to n&lt;br /&gt;
      lcs[0][j] = 0&lt;br /&gt;
    for i = 1 to m&lt;br /&gt;
      for j = 1 to n&lt;br /&gt;
        if x[i] == y[j]&lt;br /&gt;
          lcs[i][j] = lcs[i - 1][j - 1] + 1&lt;br /&gt;
          prev[i][j] = pair(i - 1, j - 1)&lt;br /&gt;
        else&lt;br /&gt;
          if a[i - 1][j] &amp;gt;= a[i][j - 1]&lt;br /&gt;
            lcs[i][j] = lcs[i - 1][j]&lt;br /&gt;
            prev[i][j] = pair(i - 1, j)&lt;br /&gt;
          else&lt;br /&gt;
            lcs[i][j] = lcs[i][j - 1]&lt;br /&gt;
            prev[i][j] = pair(i, j - 1)&lt;br /&gt;
  &lt;br /&gt;
  // вывод НОП, вызывается как printLCS(prev, x, m, n)&lt;br /&gt;
  printLCS(prev, x, i, j)&lt;br /&gt;
    if i == 0 or j == 0 // пришли к началу НОП&lt;br /&gt;
      return&lt;br /&gt;
    if prev[i][j] == pair(i - 1, j - 1) // если пришли в lcs[i][j] из lcs[i - 1][j - 1], то x[i] == y[j], надо вывести этот элемент&lt;br /&gt;
      printLCS(prev, x, i - 1, j - 1)&lt;br /&gt;
      print x[i]&lt;br /&gt;
    else&lt;br /&gt;
      if prev[i][j] == pair(i - 1, j)&lt;br /&gt;
        printLCS(prev, x, i - 1, j)&lt;br /&gt;
      else&lt;br /&gt;
        printLCS(prev, x, i, j - 1)&lt;br /&gt;
&lt;br /&gt;
== Оптимизация для вычисления только длины НОП ==&lt;br /&gt;
Заметим, что для вычисления &amp;lt;tex&amp;gt; lcs[i][j] &amp;lt;/tex&amp;gt; нужны только &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;-ая и &amp;lt;tex&amp;gt; (i-1) &amp;lt;/tex&amp;gt;-ая строчки матрицы &amp;lt;tex&amp;gt; lcs &amp;lt;/tex&amp;gt;. Тогда можно использовать лишь &amp;lt;tex&amp;gt; 2 \cdot min(m, n) &amp;lt;/tex&amp;gt; элементов таблицы:&lt;br /&gt;
&lt;br /&gt;
  LCS2(x, y)&lt;br /&gt;
    if length(x) &amp;lt; length(y) // в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y&lt;br /&gt;
      swap(x, y)&lt;br /&gt;
    m = length(x)&lt;br /&gt;
    n = length(y)&lt;br /&gt;
    for j = 0 to n&lt;br /&gt;
      lcs[0][j] = 0&lt;br /&gt;
      lcs[1][j] = 0&lt;br /&gt;
    for i = 1 to m&lt;br /&gt;
      lcs[1][0] = 0&lt;br /&gt;
      for j = 1 to n&lt;br /&gt;
        lcs[0][j] = lcs[1][j] // элемент, который был в a[1][j], теперь в предыдущей строчке&lt;br /&gt;
        if x[i] == y[j]&lt;br /&gt;
          lcs[1][j] = lcs[0][j - 1] + 1&lt;br /&gt;
        else&lt;br /&gt;
          if lcs[0][j] &amp;gt;= lcs[1][j - 1]&lt;br /&gt;
            lcs[1][j] = lcs[0][j]&lt;br /&gt;
          else&lt;br /&gt;
            lcs[1][j] = lcs[1][j - 1]&lt;br /&gt;
    // ответ — lcs[1][n]&lt;br /&gt;
&lt;br /&gt;
Также можно заметить, что от &amp;lt;tex&amp;gt; (i - 1) &amp;lt;/tex&amp;gt;-ой строчки нужны только элементы с &amp;lt;tex&amp;gt; (j - 1) &amp;lt;/tex&amp;gt;-го столбца. В этом случае можно использовать лишь &amp;lt;tex&amp;gt; min(m, n) &amp;lt;/tex&amp;gt; элементов таблицы:&lt;br /&gt;
&lt;br /&gt;
  LCS3(x, y)&lt;br /&gt;
    if length(x) &amp;lt; length(y) // в таблице будет length(y) столбцов, и если length(x) меньше, выгоднее поменять местами x и y&lt;br /&gt;
      swap(x, y)&lt;br /&gt;
    m = length(x)&lt;br /&gt;
    n = length(y)&lt;br /&gt;
    for j = 0 to n&lt;br /&gt;
      lcs[j] = 0&lt;br /&gt;
    d = 0 // d — дополнительная переменная, в ней хранится lcs[i - 1][j - 1]&lt;br /&gt;
    // в lcs[j], lcs[j + 1], …, lcs[n] хранятся lcs[i - 1][j], lcs[i - 1][j + 1], …, lcs[i - 1][n]&lt;br /&gt;
    // в lcs[0], lcs[1], …, lcs[j - 1] хранятся lcs[i][0], lcs[i][1], …, lcs[i][j - 1]&lt;br /&gt;
    for i = 1 to m&lt;br /&gt;
      for j = 1 to n&lt;br /&gt;
        tmp = lcs[j]&lt;br /&gt;
        if x[i] == y[i]&lt;br /&gt;
          lcs[j] = d + 1&lt;br /&gt;
        else&lt;br /&gt;
          if lcs[j] &amp;gt;= lcs[j - 1]&lt;br /&gt;
            lcs[j] = lcs[j] // в lcs[j] и так хранится lcs[i - 1][j]&lt;br /&gt;
          else&lt;br /&gt;
            lcs[j] = lcs[j - 1]&lt;br /&gt;
        d = tmp&lt;br /&gt;
    // ответ — lcs[n]&lt;br /&gt;
&lt;br /&gt;
== Список литературы ==&lt;br /&gt;
Т. Кормен, Ч. Лейзерсон, Р. Риверст, К. Штайн, «Алгоритмы: построение и анализ», 2-е изд., стр 418—425&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>217.66.158.116</name></author>	</entry>

	</feed>