Дерево палиндромов
Дерево палиндромов — структура данных, позволяющая решить некоторые интересные задачи на палиндромы.
Эту структуру данных придумал Михаил Рубинчик и рассказал ее на летних сборах в Петрозаводске в 2014 году.
Содержание
Описание структуры
Дерево палиндромов состоит из вершин. Каждая вершина соответствует палиндрому. Через
будем обозначать строку, которой соответствует вершина .Ребра дерева палиндромов ориентированные и помечены символами. Ребро с символом
из вершины в вершину означает, что . Тут означает конкатенацию строк.Также в дереве палиндромов присутствуют суффиксные ссылки. Суффиксная ссылка из вершины
ведет в вершину , если является наибольшим суффиксом строки .
Название структуры данных выбрано не совсем удачно, т.к. на самом деле она представляет из себя два дерева, однако суффиксные ссылки могут вести как в то же, так и в другое дерево. Это сделано для удобства реализации. Также в целях экономии памяти мы не будем хранить для каждой вершины соответствующую ей строку-палиндром. Вместо этого мы будем хранить только длину палиндрома.
Итак, у нас будет два дерева. Одно дерево для палиндромов четной длины, другое — для палиндромов нечетной длины. Каждое дерево будет иметь фиктивный корень. Обозначим корни четного и нечетного дерева соответственно
и .будет соответствовать фиктивному палиндрому длины -1. Это нужно для того, чтобы не обрабатывать отдельно случай добавления палиндрома длины 1. Теперь каждый раз при добавлении новой вершины к вершине мы будем просто указывать ее длину равной .
будет соответствовать фиктивному палиндрому длины 0.
Также направим суффиксные ссылки обоих корней к вершине
. Это нужно для того, чтобы .Построение
Будем обрабатывать строку символ за символом. Пусть мы уже обработали некоторый префикс
и теперь хотим добавить следующий символ строки, назовем его .Будем также поддерживать максимальный палиндром-суффикс обработанного префикса
. Назовем его .Т.к.
находится в уже обработанной части строки, то ему соответствует какая-то вершина в дереве. У этой вершины есть суффиксная ссылка на какую-то другую вершину, у которой тоже есть суффиксная ссылка и т.д.Найдем теперь палиндром-суффикс нового префикса, состоящего из
и . Искомый палиндром-суффикс будет иметь вид , где — это какая-то строка, возможно пустая (или фиктивный строка длины -1, соответствующая корню , если искомый палиндром-суффикс — это просто символ ). Т.к. палиндром, то — тоже палиндром, и, более того, это суффикс строки . Поэтому он может быть достигнут из по суффиксным ссылкам.Строка
— это единственная подстрока-палиндром строки , которой, возможно, нет в (все другие подстроки-палиндромы есть). Чтобы понять почему это так, нужно обратить внимание на то, что все новые подстроки-палиндромы, которых не было в , должны оканчиваться на символ , и поэтому должны быть палиндромом-суффиксом строки . Из-за того, что — наибольший палиндром-суффикс строки , все остальные меньшие палиндромы-суффиксы этой строки уже есть в каком-то префиксе строки (т.к. для каждого суффикса палиндрома есть равный ему префикс) и, соответственно, уже есть в .Таким образом, чтобы обработать очередной символ
, нужно просто спуститься по суффиксным ссылкам строки до тех пор, пока мы не найдем подходящую строку (причем мы всегда можем найти такую строку, возможно длины -1, если очередная суффиксная ссылка будет вести в корень). Затем нужно проверить, есть ли уже ребро по символу из вершины, соответствующей , и если нет, добавить это ребро в новую вершину .Теперь нужно добавить суффиксную ссылку из вершины
. Если эта вершина уже существовала до добавления символа , ничего делать не нужно — суффиксная ссылка итак указывает на правильную вершину. Иначе на нужно найти наибольший палиндром-суффикс строки , который будет иметь вид , где — это некоторая строка, возможно, пустая. Следуя той же логике, которую мы использовали раньше, — это палиндром-суффикс строки и может быть достигнут из по суффиксным ссылкам.