Задача о наименьшей суперпоследовательности — различия между версиями
Motyaspr (обсуждение | вклад) |
Motyaspr (обсуждение | вклад) |
||
| Строка 60: | Строка 60: | ||
prev[i][j] = pair(i, j - 1) | prev[i][j] = pair(i, j - 1) | ||
| − | + | '''fun''' printLCS(m: '''int''', n: '''int'''): ''<font color="green">// вывод SCS</font>'' | |
| − | + | i = m | |
| − | + | j = n | |
| − | ''' | + | ans = [] ''<font color="green">// массив ответа </font>'' |
| − | + | '''while''' i > 0 '''and''' j > 0 | |
| − | + | '''if''' prev[i][j] == pair(i - 1, j - 1) | |
| − | + | ans.append(x[i]) | |
| − | '''if''' prev[i][j] == pair(i - 1, j) | + | i -= 1 |
| − | + | j -= 1 | |
'''else''' | '''else''' | ||
| − | + | '''if''' prev[i][j] == pair(i - 1, j) | |
| + | ans.append(x[i]) | ||
| + | i -= 1 | ||
| + | '''else''' | ||
| + | ans.append(y[j]) | ||
| + | j -= 1 | ||
| + | '''while''' i > 0 ''<font color="green">// добавляем оставшиеся символы первой последовательности </font>'' | ||
| + | ans.append(x[i]) | ||
| + | i -= 1 | ||
| + | '''while''' j > 0 | ||
| + | ans.append(y[j]) ''<font color="green">// добавляем оставшиеся символы второй последовательности </font>'' | ||
| + | j -= 1 | ||
| + | '''reverse'''(ans) ''<font color="green">// разворачиваем последовательность, так как шли с конца </font>'' | ||
| + | '''return''' ans | ||
| + | |||
| + | ==Связь с наибольшей общей подпоследовательностью== | ||
Версия 19:50, 23 декабря 2017
| Определение: |
| Последовательность является суперпоследовательностью (англ. supersequence) последовательности , если существует строго возрастающая последовательность индексов таких, что для всех выполняется соотношение . |
| Определение: |
| Последовательность является общей суперпоследовательностью (англ. common supersequence) последовательностей и , если и являются подпоследовательностями . |
| Задача: |
| Пусть имеются последовательности и . Необходимо найти |
Содержание
Наивное решение
Заметим, что если приписать к одной из данной последовательность другую, то полученная последовательность будет их суперпоследовательностью с длиной . Запомним все элементы обеих последовательностей и из них построим все возможные последовательности. Тогда искомая гарантированно найдётся, однако время работы алгоритма будет экспоненциально зависеть от длины исходных последовательностей.
Динамическое программирование
Решение
Обозначим за SCS префиксов данных последовательностей и , заканчивающихся в элементах с номерами и соответственно. Наименьшая общая суперпоследовательность и должна содержать каждый символ обеих последовательностей, поэтому если , то это просто последовательность . Аналогичен случай, когда . Если и , то возможны два случая. Если , то SCS должна включать оба этих элемента. Значит нужно выбрать оптимальный из ответов для префиксов, включающих один элемент и не включающих второй, и выбрать из них минимальный. Если же , то SCS для последовательностей и должна заканчиваться этим элементом, так как он общий для них. Получается следующее рекуррентное соотношение:
Очевидно, что сложность алгоритма составит , где и — длины последовательностей.
Восстановление ответа
В помимо длины последовательности хранятся и символ, добавленный последним. Таким образом, зная длину SCS, можно восстановить и саму последовательность.
Псевдокод
x, y — данные последовательности; — SCS для префикса длины i последовательности x и префикса длины j последовательности y; prev[i][j] — пара индексов элемента таблицы, которые предшествовали .
fun SCS(x: int, y: int): // аналог void
m = x.size
n = y.size
for i = 1 to m
scs[i][0] = i
for j = 0 to n
scs[0][j] = j
for i = 1 to m
for j = 1 to n
if x[i] == y[j]
scs[i][j] = 1 + scs[i - 1][j - 1]
prev[i][j] = pair(i - 1, j - 1)
else
if scs[i - 1][j] <= lcs[i][j - 1]
scs[i][j] = 1 + scs[i - 1][j]
prev[i][j] = pair(i - 1, j)
else
scs[i][j] = 1 + scs[i][j - 1]
prev[i][j] = pair(i, j - 1)
fun printLCS(m: int, n: int): // вывод SCS
i = m
j = n
ans = [] // массив ответа
while i > 0 and j > 0
if prev[i][j] == pair(i - 1, j - 1)
ans.append(x[i])
i -= 1
j -= 1
else
if prev[i][j] == pair(i - 1, j)
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