Дерево палиндромов

Материал из testwiki
Перейти к навигации Перейти к поиску

Шаблон:Структура данных

Дерево палиндромов (Шаблон:Lang-en, также овердрево[1], Шаблон:Lang-en) — структура данных, предназначенная для хранения и обработки палиндромных подстрок некоторой строки. Была предложена учёными из Уральского федерального университета Михаилом Рубинчиком и Арсением Шуром в 2015 году. Представляет собой два префиксных дерева, собранных из правых «половинок» палиндромных подстрок чётной и нечётной длины соответственно. Структура занимает O(n) памяти и может быть построена за время O(nlogσ), где n — длина строки, а σ — количество различных символов в ней. С помощью дерева палиндромов можно эффективно решать такие задачи, как подсчёт числа различных палиндромных подстрок, поиск разбиения строки на наименьшее число палиндромов, проверка подстроки на то, является ли она палиндромом, и другие.

Обозначения

Пусть S=s1s2sn — некоторая строка, а SR=snsn1s1 — обращённая строка S. При описании дерева палиндромов строки S используются следующие обозначения[2]:

  • Строка S называется палиндромом, если она читается одинаково слева направо и справа налево, то есть если S=SR.
  • В частности, подстрока, у которой l=1, называется префиксом строки S, а подстрока, у которой r=n, — суффиксом строки S.
  • Палиндромной подстрокой (подпалиндромом) называют подстроку S, которая является палиндромом. Если эта подстрока также является префиксом или суффиксом строки S, то её называют префикс- или суффикс-палиндромом соответственно.
  • Каждой вершине префиксного дерева соответствует строка, равная конкатенации символов на пути из корня дерева в эту вершину.

Структура дерева

В обозначениях выше, дерево палиндромов строки S — это ориентированный граф, каждая вершина которого соответствует некоторому уникальному подпалиндрому строки и отождествляется с ним. Если у строки есть подпалиндромы t и ctc, где c — некоторый символ алфавита, то в дереве палиндромов есть дуга, помеченная символом c, из вершины, соответствующей t, в вершину, соответствующую ctc. В таком графе у любой вершины может быть только одна входящая дуга. Для удобства также вводятся две служебные вершины, которые соответствуют палиндромам длины 0 (пустая строка) и 1 («мнимая» строка) соответственно. Дуги из пустой строки ведут в вершины, соответствующие палиндромам вида cc, а из «мнимой строки» — в вершины, соответствующие палиндромам вида c (то есть состоящим из единственного символа). Вершина называется чётной, если ей соответствует палиндром чётной длины, и нечётной в противном случае. Из определения следует, что дуги в дереве палиндромов проходят только между вершинами с одинаковой чётностью. С точки зрения префиксных деревьев данная структура может быть описана следующим образом[3]:

Шаблон:Теорема

Количество вершин в дереве палиндромов не превосходит n+2, что является прямым следствием следующей леммы[4]:

Шаблон:Теорема Шаблон:Доказательство Помимо обычных дуг, которые служат переходами для префиксного дерева, для каждой вершины дерева палиндромов определяется суффиксная ссылка, которая ведёт из вершины v в вершину u, соответствующую наибольшему собственному (не равному всей строке v) суффикс-палиндрому v. При этом суффиксная ссылка из «мнимой» вершины не определена, а из пустой вершины по определению ведёт в «мнимую». Суффиксные ссылки образуют дерево с корнем в «мнимой» вершине и играют важную роль в построении дерева палиндромов[3].

Построение

Как и многие другие строковые структуры, дерево палиндромов строится итеративно. Изначально оно состоит лишь из вершин, соответствующих пустой и мнимой строкам. Затем структура постепенно перестраивается при наращивании строки по одному символу. Так как при добавлении одного символа в строке появляется не более одного нового палиндрома, перестройка дерева в худшем случае потребует добавления одной новой вершины и суффиксной ссылки к ней. Для определения возможной новой вершины в ходе построения дерева поддерживается указатель Шаблон:Math на вершину, соответствующую наибольшему из текущих суффикс-палиндромов[3].

Все суффикс-палиндромы строки достижимы по суффиксным ссылкам из Шаблон:Math, поэтому для определения нового суффикс-палиндрома (именно он будет соответствовать новой вершине, если таковая появится) необходимо переходить по суффиксным ссылкам Шаблон:Math, пока не обнаружится, что символ, предшествующий текущему суффикс-палиндрому, совпадает с символом, который был приписан к строке. Более формально, пусть P — максимальный суффикс-палиндром строки S1,k=s1s2sk, тогда либо P=sk, либо P=skQsk, где Q — некоторый суффикс-палиндром S1,k1. Таким образом, перебирая Q среди суффиксных ссылок Шаблон:Math, можно определить, может ли он быть расширен до P путём сравнения символов sk|Q|1 и sk. Когда соответствующий суффикс-палиндром Q был найден, следует проверить, присутствует ли в дереве палиндромов переход из соответствующей ему вершины по символу sk[3].

Если такой переход есть, то P уже встречался в строке ранее и соответствует вершине, в которую ведёт этот переход. В противном случае необходимо создать для него новую вершину и провести переход по sk из Q. Затем следует определить суффиксную ссылку для P, которая соответствует второму максимальному суффикс-палиндрому S1,k. Для того, чтобы её найти, следует продолжать обход суффиксных ссылок Шаблон:Math, пока не встретится вторая вершина Q, такая что sk|Q|1=sk; именно эта вершина и будет суффиксный ссылкой P. Если обозначить переход из вершины v по символу c как δ(v,c), весь процесс может быть описан следующим псевдокодом[3]:

Шаблон:Math
    Шаблон:Math
        Шаблон:Math
    Шаблон:Math

Шаблон:Math
    Шаблон:Math
    Шаблон:Math
    Шаблон:Math
    Шаблон:Math
        Шаблон:Math
        Шаблон:Math
        Шаблон:Math
        Шаблон:Math
    Шаблон:Math

Здесь предполагается, что изначально дерево описывается лишь двумя вершинами с длинами 0 и 1 соответственно с суффиксной ссылкой из первой вершины во вторую. В переменной Шаблон:Math хранится вершина, соответствующая наибольшему суффикс-палиндрому текущей строки, изначально она указывает на вершину нулевой строки. Также предполагается, что k изначально равно 0 и в s0 записан некоторый служебный символ, который не встречается в строке s1s2sk.

Вычислительная сложность

Сложность алгоритма может варьировать в зависимости от структур данных, в которых хранится таблица переходов в дереве. В общем случае при использовании ассоциативного массива время, затрачиваемое на обращение к δ(q,c), достигает O(logσ), где σ — размер алфавита, из символов которого построена строка. Стоит заметить, что каждая итерация первого вызова Шаблон:Math уменьшает длину Шаблон:Math, а второго — длину Шаблон:Math, которые между последовательными вызовам Шаблон:Math могут увеличиться только на единицу. Таким образом, суммарное время работы Шаблон:Math не превосходит O(n), а общее время, требуемое для выполнения n вызовов Шаблон:Math, можно оценить как O(nlogσ)[3]. Расход памяти у данной структуры в худшем случае линейный, однако если рассматривать усреднённый размер структуры по всем строкам заданной длины n, средний расход памяти будет порядка O(nσ)[5].

Модификации

Одновременно с введением данной структуры данных Рубинчик и Шур также предложили ряд модификаций, позволяющих расширить область задач, решаемых деревом палиндромов. В частности, был предложен метод, позволяющий с той же асимптотикой построить общее дерево палиндромов для множества строк S1,S2,,Sk. Такая модификация позволяет решать те же задачи, рассматриваемые в контексте множества строк — например, найти наибольший общий подпалиндром всех строк или число различных подпалиндромов всех строк в совокупности. Другой предложенной модификацией стал вариант построения дерева, при котором на добавление одного символа требуется O(logn) времени в худшем случае (а не O(logσ) амортизированно, как это происходит в стандартном построении) и O(1) памяти. Такой подход позволяет обеспечить Шаблон:Iw дерева, при которой можно в произвольные моменты времени откатывать добавление последнего символа. Кроме того, была предложена полностью персистентная версия дерева, позволяющая обратиться и дописать символ к любой из сохранённых ранее версий за O(logn) времени и памяти в худшем случае[6].

В 2019 году Ватанабе с коллегами разработали на основе дерева палиндромов структуру данных, называемую Шаблон:Math, для работы с подпалиндромами строк, заданных кодированием длин серий[4], а в 2020 году тот же состав авторов, совместно с Миено, разработали два алгоритма, позволяющих поддерживать дерево палиндромов на скользящем окне размера d. Первый из указанных алгоритмов требует O(nlogσ) времени и O(d) памяти, а второй — O(n+dσ) времени и O(dσ) памяти[7].

Применения

Дерево палиндромов даёт множество возможных применений для получения теоретически быстрых и практически легко реализуемых алгоритмов для решения ряда комбинаторных задач в программировании и математической кибернетике[8].

Одной из задач, для которых была разработана данная структура, является подсчёт различных подпалиндромов в строке Шаблон:Iw. Она может быть поставлена следующим образом: к изначально пустой строке поочерёдно приписывается по одному символу. На каждом шаге необходимо вывести число различных подпалиндромов в данной строке. С точки зрения дерева палиндромов это эквивалентно тому, чтобы на каждом шаге вывести количество нетривиальных вершин в структуре. Линейное решение для оффлайн-версии данной задачи было представлено в 2010 году[9], а оптимальное решение со временем исполнения O(nlogσ) для онлайн-версии было найдено в 2013 году[10]. Указанное решение, однако, использовало две «тяжеловесные» структуры данных — аналог алгоритма Манакера, а также суффиксное дерево. Дерево палиндромов же, с одной стороны, имеет ту же асимптотику в худшем случае, а с другой — является значительно более легковесной структурой[3].

Другим возможным применением данной структуры является перечисление палиндромно-богатых двоичных строк[11]. Ранее было показано, что слово длины n может содержать не более n+1 различных палиндромов, палиндромно-богатыми называются слова, на которых данная оценка достигается. Понятие палиндромно-богатых слов было введено Эми Глен и коллегами в 2008 году[12]. Рубинчик и Шур показали, что с помощью дерева палиндромов можно обнаружить все палиндромно-богатые слова, чья длина не превосходит n за O(R), где R — количество таких слов. Данный результат позволил увеличить количество известных членов последовательности Шаблон:OEIS short в OEIS c 25 до 60. Полученные данные показали, что последовательность растёт значительно медленнее, чем это предполагалось ранее, а именно она ограничена сверху как O(1,605n)[13].

Примечания

Шаблон:Примечания

Литература

Шаблон:Refbegin

Ссылки

Шаблон:Строки Шаблон:Хорошая статья