Задача о наименьшей суперпоследовательности
Определение: |
Последовательность | является суперпоследовательностью (англ. 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] <= scs[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я суперпоследовательностью , то можно представить так: Мы должны поставить на место некоторых пропусков поставить элементы , так чтобы суммарная длина была минимальна, и был суперпоследовательностью . Рассмотрим любую общую подпоследовательность и .Заметим, что она уже находятся в , а значит все её элементы не нужно добавлять. Поэтому мы добавим не меньше чем . Длину нужно минимизировать, значит имеет место равенство: . |