Алгоритм Маначера используется для поиска подпалиндромов в заданной строке за линейное время. Более формально постановка задачи выглядит следующим образом: Дана строка s длины n. Найдите все пары (i,j) таких, что подстрока s[i…j] является палиндромом. Строка t является палиндромом, если ( - обратная строка для ).
t=trev
trev
t
Алгоритм
Простой подход
Прямым решением может быть итерация по каждому символу заданной строки и рассмотрение его как центра палиндрома, то есть просмотр соседних символов и увеличение позиций на столько, на сколько это возможно, сравнивая каждый раз пару соответствующих символов. Чтобы избежать переполнения и прохождения левой и правой границ, мы можем добавить терминальные обозначения этих границ.
Реализация тривиального алгоритма выглядит следующим образом:
Терминальные символы $ и ^ были использованы для того, чтобы не обрабатывать концы строки отдельно.
Алгоритм Манахера для строки нечетной длины
Пусть dodd[i] обозначает палиндромы нечетной длины с центром в i. Пусть (l,r) - границы самого правого найденного (под)палиндрома (то есть текущий самый правый (под)палиндром - s[l+1]s[l+2]…s[r−1]). Изначально l=0,r=1, что является пустой строкой.
Далее мы хотим вычислить dodd[i], поэтому:
Если i находится за пределами текущего подпалиндрома, то есть i≥r, мы можем использовать простой подход, описанный выше.
Если i≤r, то найдем "зеркальную" позицию i в подпалиндроме (l,r), то есть получим позицию j=l+(r−i), и проверим значение
dodd[j]. Поскольку j - это позиция, симметричная i относительно (l+r)/2, мы почти всегда можем определить dodd[i]=dodd[j]. Иллюстрация этого (палиндром вокруг
j фактически "копируется" в палиндром вокруг
i):
….
Сложным может быть случай, когда "внутренний" палиндром достигает границ "внешнего", т.е.
j−dodd[j]≤l (или, что то же самое,
i+dodd[j]≥r). Поскольку симметрия вне "внешнего" палиндрома не гарантированно, простое присвоение
dodd[i]=dodd[j] будет неверным: у нас нет достаточных данных, чтобы утверждать, что палиндром в позиции
i имеет одинаковую длину.
Для корректной работы с такими ситуациями нам пока следует ограничить длину палиндрома, то есть пусть dodd[i]=r−i, . После этого мы запустим описанный выше простой подход, который попытается увеличить
dodd[i], пока это возможно.
Иллюстрация этого случая (палиндром с центром
j ограничен, чтобы вписаться во "внешний" палиндром):
…palindromesl+1…sj…palindromesi−(r−i)+1…palindromeпопробуйтепереместитьсясюда……………….
На рисунке показано, что хотя палиндром с центром
j мог бы быть больше и выходить за пределы "внешнего" палиндрома, но с
i в качестве центра мы можем использовать только ту часть, которая полностью помещается во "внешний" палиндром. Но ответ для позиции
i (
dodd[i]) может быть гораздо больше этой части, поэтому далее мы запустим наш тривиальный алгоритм, который попытается вырастить его за пределы нашего "внешнего" палиндрома, то есть в область "попробуйте переместить сюда". Напоминаем, что для корректной работы нам следует обновлять значения (l,r) после вычисления каждого dodd[i].
Реализация для алгоритма Манахера нечетной длины:
Алгоритм Манакера для общего случая
Хотя мы могли бы использовать описанный выше подход и модифицировать его, чтобы рассматривать два символа как центры строки четной длины, можно свести всю проблему к случаю, когда мы имеем дело только с палиндромами нечетной длины. Для этого мы можем поместить дополнительный символ # между каждой буквой в строке, а также в начале и конце строки. Это сохранит свойства существующих палиндромов и сделает строку нечетной:
Здесь d[2i]=2deven[i]+1 и d[2i+1]=2dodd[i], где d обозначает массив Манахера для палиндромов нечетной длины в #-смежной строке, а dodd и deven соответствуют массивам, определенным выше в исходной строке.
Алгоритм с корректировкой строки может быть реализован следующим образом: