Задача о наименьшей суперпоследовательности — различия между версиями
Motyaspr (обсуждение | вклад) (→Наивное решение) |
Motyaspr (обсуждение | вклад) |
||
Строка 34: | Строка 34: | ||
===Восстановление ответа=== | ===Восстановление ответа=== | ||
− | Для восстановления ответа заведем массив <tex> prev[0 \dots n][0\dots m] </tex>, где <tex>prev[i][j]</tex> будет | + | Для восстановления ответа заведем массив <tex> prev[0 \dots n][0\dots m] </tex>, где <tex>prev[i][j]</tex> будет равняться: |
+ | |||
+ | <tex> | ||
+ | prev[i][j] = | ||
+ | \begin{cases} | ||
+ | 1, & x[i] == y[j] \\ | ||
+ | 2, & x[i] \neq y[j], sc[i - 1][j] > sc[i][j - 1] \\ | ||
+ | 3, & x[i] \neq y[j], sc[i - 1][j] < sc[i][j - 1] \\ | ||
+ | \end{cases} | ||
+ | </tex> | ||
+ | |||
+ | По этим данным можно узнать, какой символ был добавлен в наименьшую общую суперпоследовательность. | ||
===Псевдокод=== | ===Псевдокод=== | ||
Строка 50: | Строка 61: | ||
'''if''' x[i] == y[j] | '''if''' x[i] == y[j] | ||
scs[i][j] = 1 + scs[i - 1][j - 1] | scs[i][j] = 1 + scs[i - 1][j - 1] | ||
− | prev[i][j] = | + | prev[i][j] = 1 |
'''else''' | '''else''' | ||
'''if''' scs[i - 1][j] <= lcs[i][j - 1] | '''if''' scs[i - 1][j] <= lcs[i][j - 1] | ||
scs[i][j] = 1 + scs[i - 1][j] | scs[i][j] = 1 + scs[i - 1][j] | ||
− | prev[i][j] = | + | prev[i][j] = 2 |
'''else''' | '''else''' | ||
scs[i][j] = 1 + scs[i][j - 1] | scs[i][j] = 1 + scs[i][j - 1] | ||
− | prev[i][j] = | + | prev[i][j] = 3 |
'''fun''' printLCS(n: '''int''', m: '''int'''): ''<font color="green">// вывод SCS</font>'' | '''fun''' printLCS(n: '''int''', m: '''int'''): ''<font color="green">// вывод SCS</font>'' | ||
Строка 64: | Строка 75: | ||
ans = [] ''<font color="green">// массив ответа </font>'' | ans = [] ''<font color="green">// массив ответа </font>'' | ||
'''while''' i > 0 '''and''' j > 0 | '''while''' i > 0 '''and''' j > 0 | ||
− | '''if''' prev[i][j] == | + | '''if''' prev[i][j] == 1 |
ans.append(x[i]) | ans.append(x[i]) | ||
i -= 1 | i -= 1 | ||
j -= 1 | j -= 1 | ||
'''else''' | '''else''' | ||
− | '''if''' prev[i][j] == | + | '''if''' prev[i][j] == 2 |
ans.append(x[i]) | ans.append(x[i]) | ||
i -= 1 | i -= 1 |
Версия 23:25, 23 декабря 2017
Определение: |
Последовательность | является суперпоследовательностью (англ. supersequence) последовательности , если существует строго возрастающая последовательность индексов таких, что для всех выполняется соотношение .
Определение: |
Последовательность | является общей суперпоследовательностью (англ. common supersequence) последовательностей и , если является суперпоследовательностью как для , так для и .
Задача: |
Пусть имеются последовательности | и . Необходимо найти
Содержание
Наивное решение
Пусть даны две последовательности длины
и соответственно. Заметим, что если приписать к одной из данных последовательностей другую, то полученная последовательность будет их суперпоследовательностью с длиной . Запомним все элементы обеих последовательностей и из них построим все возможные последовательности. Тогда искомая гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длин исходных последовательностей.Динамическое программирование
Решение
Обозначим за
префиксов данных последовательностей и , заканчивающихся в элементах с номерами и соответственно. Наименьшая общая суперпоследовательность и должна содержать каждый символ обеих последовательностей, поэтому если , то это просто последовательность . Аналогичен случай, когда . Если и , то возможны два случая. Если , то SCS должна включать оба этих элемента. Значит нужно выбрать минимальный из ответов для префиксов, включающих один элемент и не включающих второй. Если же , то для последовательностей и должна заканчиваться этим элементом, так как он общий для них. Получается следующее рекуррентное соотношение:
Cложность алгоритма составит
, где и — длины последовательностей.Восстановление ответа
Для восстановления ответа заведем массив
, где будет равняться:
По этим данным можно узнать, какой символ был добавлен в наименьшую общую суперпоследовательность.
Псевдокод
x, y — данные последовательности;
— SCS для префикса длины i последовательности x и префикса длины j последовательности y; prev[i][j] — пара индексов элемента таблицы, которые предшествовали .fun SCS(x: int, y: int): // аналог void n = x.size m = y.size for i = 1 to n scs[i][0] = i for j = 0 to m scs[0][j] = j for i = 1 to n for j = 1 to m if x[i] == y[j] scs[i][j] = 1 + scs[i - 1][j - 1] prev[i][j] = 1 else if scs[i - 1][j] <= lcs[i][j - 1] scs[i][j] = 1 + scs[i - 1][j] prev[i][j] = 2 else scs[i][j] = 1 + scs[i][j - 1] prev[i][j] = 3 fun printLCS(n: int, m: int): // вывод SCS i = n j = m ans = [] // массив ответа while i > 0 and j > 0 if prev[i][j] == 1 ans.append(x[i]) i -= 1 j -= 1 else if prev[i][j] == 2 ans.append(x[i]) i -= 1 else ans.append(y[j]) j -= 1 while i > 0 // добавляем оставшиеся символы первой последовательности ans.append(x[i]) i -= 1 while j > 0 ans.append(y[j]) // добавляем оставшиеся символы второй последовательности j -= 1 reverse(ans) // разворачиваем последовательность, так как шли с конца return ans
Связь с наибольшей общей подпоследовательностью
Теорема: |
наибольшей общей подпоследовательности, - длина наименьшей общей суперпоследовательности, и - длины последовательностей и соответсвенно. , где - длина |
Доказательство: |
Пусть Поэтому: , . Обозначим за их SCS и будем ее строить. Так как являетcя суперпоследовательностью , то можно представить так: Мы должны поставить на место некоторых пропусков поставить элементы , так чтобы суммарная длина была минимальна, и был суперпоследовательностью . Рассмотрим любую общую подпоследовательность и .Заметим, что она уже находятся в , а значит все её элементы не нужно добавлять. Поэтому мы добавим не меньше чем . Длину нужно минимизировать, значит имеет место равенство: . |