Изменения

Перейти к: навигация, поиск
1
{{В разработке}}
Задача о '''наибольшей подпоследовательности-палиндрома''' — это задача поиска длины наибольшей подпоследовательности-палиндрома, которую можно получить вычеркиванием некоторых букв из данной последовательности.
 
== Определения ==
 
{{Определение|definition='''Палиндромом''' называется строка, которая одинаково читается как слева направо, так и справа налево.}}
'''''Например''''', '''''HELOLEH''''' является подпоследовательностью-палиндромом строки '''''HTEOLFEOLEH'''''.
== Решение (алгоритм Манакера) ==[[Файл:Palindrome11.png|200px|thumb|right|Массив длин подпоследовательностей-палиндромов]][[Файл:Palindrome12.png|200px|thumb|right|Наглядный массив переходов]]Обозначим данную последовательность через <tex>S</tex>, а ее элементы — через <tex>S[i], 1 \le i \le n</tex> Будем рассматривать возможные подпоследовательности данной последовательности с <tex>i - </tex>го по <tex>j-</tex>ый символ, обозначим их Сначала поймем как <tex>S(i, j)</tex>. Длины максимальных палиндромов для подпоследовательностей будем записывать в квадратный массив <tex>L</tex>: <tex>L[i][j]</tex> — длина максимальной находить все подпоследовательности-палиндрома, который можно получить из подпоследовательности <tex>S(iпалиндромы нечётной длины, j)</tex>тНачнем решать задачу с простых подпоследовательностейе. Для последовательности из одного элемента (то есть подпоследовательности вида <tex>S(i, i)</tex>) ответ очевиден — ничего вычеркивать не надо, такая строка будет искомой подпоследовательностью-палиндромом. Для последовательности из двух элементов <tex>S(i, i + 1)</tex> возможны два варианта: если элементы равны, то мы имеем подпоследовательность-палиндром, ничего вычеркивать не надо. Если же элементы не равны, то вычеркиваем любой. Пусть теперь нам дана подпоследовательность <tex>S(i, j)</tex>. Если первый <tex>(S[i])</tex> и последний <tex>(S[j])</tex> элементы подпоследовательности не совпадают, то один из них нужно вычеркнуть. Тогда у нас останется подпоследовательность <tex>S(i, j - 1)</tex> или <tex>S(i + 1, j)</tex> — то есть мы сведем задачу к подзадаче: вычислять массив <tex>L[i][j] = max(L[i]d_1[j - 1], L[i + 1][j])</tex>. Если же первый и последний элементы равны, то мы можем оставить оба, но необходимо знать ; решение задачи <tex>Sдля палиндромов чётной длины (i + 1, j - 1): L[i][j] = L[i + 1][j - 1] + 2</tex>т== Пример ==Рассмотрим решение на примере последовательности '''''ABACCBA'''''е. Первым делом заполняем диагональ нахождение массива единицами, они будут соответствовать подпоследовательностями <tex>S(i, i)</tex> из одного элемента. Затем начинаем рассматривать подпоследовательности длины два. Во всех подпоследовательностях, кроме <tex>S(4, 5)</tex>, элементы различны, поэтому в соответствующие ячейки запишем <tex>1</tex>, а в <tex>Ld_2[4][5]</tex> — <tex>2</tex>. Получается, что мы будем заполнять массив по диагоналям, начиная с главной диагонали, ведущей из левого верхнего угла в правый нижний) получится небольшой модификацией этого. Для подпоследовательностей длины <tex>3</tex> получаются следующие значения: в подпоследовательности '''''ABA''''' первый и последний элемент равны, поэтому <tex>L[1][3] = L[2][2] + 2</tex>. В остальных подпоследовательностях первый и последний элементы различны. '''''BAC''''': <tex>L[2][4] = max(L[2][3], L[3][4]) = 1</tex>
Для быстрого вычисления будем поддерживать '''границы''ACC''''': <tex>L[3][5] = max(L[3][4]l, L[4][5]r) </tex> самого правого из обнаруженных палиндромов (т.е. палиндрома с наибольшим значением <tex>r</tex>). Изначально можно считать <tex>l=0, r= 2-1</tex>.
'''''CCB''''': Итак, пусть вычисляем значение <tex>Ld_1[4][6] = max(L[4][5i]</tex> для очередного <tex>i</tex>, L[5]при этом все предыдущие значения <tex>d_1[6]) = 2</tex>уже подсчитаны.
'''''CBA''''': * Если <tex>i</tex> не находится в пределах текущего палиндрома, т.е. <tex>i>r</tex>, то просто выполним тривиальный алгоритм. Т.е. будем последовательно увеличивать значение <tex>Ld_1[5i]</tex>, и проверять каждый раз — правда ли текущая подпоследовательность <tex>[7i-d_1[i] = max(L;i+d_1[5i][6]</tex> является палиндромом. Когда найдем первое расхождение, Lлибо когда дойдем до границ строки <tex>S</tex> — останавливаемся: окончательно посчитали значение <tex>d_1[6][7i]</tex>. После этого мы должны не забыть обновить значения <tex>(l,r) = 1</tex>.
Продолжая далее аналогичные рассуждения* Рассмотрим случай, заполним все ячейки под диагональю и в ячейке когда <tex>i \le r</tex>. Попробуем извлечь часть информации из уже подсчитанных значений <tex>Ld_1[1][7]</tex> . А именно, отразим позицию <tex>i</tex> внутри палиндрома <tex>(l,r)</tex>, т.е. получим ответ позицию <tex>j=l+(6r-i)</tex>, и рассмотрим значение <tex>d_1[j]</tex>.Поскольку <tex>j</tex> — позиция, симметричная позиции <tex>i</tex>, то почти всегда можно просто присвоить <tex>d_1[i]=d_1[j]</tex>. Иллюстрация этого отражения (палиндром вокруг фактически "копируется" в палиндром вокруг <tex>i</tex>):'''IMG1'''
Если же в задаче необходимо вывести не длину, а саму подпоследовательность-палиндром, то дополнительно к массиву длин мы должны построить массив переходов — для каждой ячейки запомнить, какой из случаев был реализован.
== Псевдокод ==
Размещаем граничные случаи: <tex>F(I, I) = 1</tex> и <tex>F(I, J) = 0</tex>, если <tex>I > J</tex>. Теперь в рассматриваемой подстроке отделяем первый символ. Есть две возможности (какая из них реализуется, пока не знаем). Отделенный символ не участвует в образовании максимального подпоследовательности-палиндрома — тогда <tex>F(I, J) = F(I + 1, J)</tex>. Если же участвует, то ищем в подстроке с конца символ, совпадающий с отделенным (пусть его позиция <tex>K</tex>), тогда <tex>F(I, J) = 2 + F(I + 1, K – 1)</tex>. Надо предусмотреть еще случай <tex>I = K</tex>, а затем отсекать рекурсию.
Получается следующая функция:
Function F(i, j)
if L[i, j] == -1
k = j
while S[i] <> S[k]
k--
R1 = F(i + 1, j)
if i <> k
R2 = F(i + 1, k - 1) + 2
else
R2 = 1
if R1 > R2
L[i, j] = R1
else
L[i, j] = R2
return L[i, j]
== См. также ==
299
правок

Навигация