Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

e-maxx_algo

.pdf
Скачиваний:
122
Добавлен:
03.06.2015
Размер:
6.19 Mб
Скачать

по суффиксному автомату и наоборот

Докажем две теоремы, устанавливающие взаимную связь между суффиксным автоматом и суффиксным деревом.

Сразу оговоримся, что мы считаем, что входная строка такова, что каждый суффикс имеет собственную вершину в суффиксном дереве (поскольку для произвольных строк это, вообще говоря, неверно: например, для строки ). Обычно этого добиваются путём приписывания в конец строки какого-нибудь особого символа (обычно обозначаемого через знак доллара).

Для удобства введём обозначения: — это строка , записанная в обратном порядке,

— это

суффиксный автомат, построенный для строки , — это суффиксное дерево строки .

Введём понятие расширяющей ссылки: зафиксируем вершину суффиксного дерева и символ ; тогда расширяющая ссылка ведёт в вершину дерева, соответствующую строке (если этот путь

оканчивается посередине ребра, то проведём ссылку в нижний конец этого ребра); если такого пути вообще нет в дереве, то расширяющая ссылка не определена. В некотором смысле, расширяющие ссылки противоположны суффиксным ссылкам.

Теорема 1. Дерево, образованное суффиксными ссылками в , является суффиксным деревом .

Теорема 2.

— это граф расширяющих ссылок суффиксного дерева

. Кроме того,

сплошные рёбра в — это инвертированные суффиксные ссылки в .

Эти две теоремы позволяют по одной из структур (суффиксному дереву или суффиксному автомату) построить другую за время — эти два простых алгоритма будут рассмотрены нами ниже в теоремах 3 и 4.

В целях наглядности, приведём суффиксный автомат с его деревом суффиксных ссылок и соответствующее суффиксное дерево для инвертированной строки. Для примера возьмём строку .

и его дерево суффиксных ссылок (для наглядности мы подписываем каждое состояние

его -строкой):

:

Лемма. Следующие три утверждения эквивалентны для любых двух подстрок и :

в строке

в строке

и лежат на одном и том же пути из корня в суффиксном дереве .

Доказательство её довольно очевидно: если начала вхождений двух строк совпадают, то одна строка является префиксом другой, а, значит, одна строка лежит в суффиксном дереве на пути другой строки.

Доказательство теоремы 1.

Состояния суффиксного автомата соответствуют вершинам суффиксного дерева.

Рассмотрим произвольную суффиксную ссылку

. Согласно определению суффиксной

ссылки,

является суффиксом

, причём среди всех таких

выбирается тот, у

которого

максимально.

 

 

 

В терминах инвертированной строки

это означает, что суффиксная ссылка

ведёт в такой длиннейший

префикс строки, соответствующей состоянию

, чтобы этому префиксу соответствовало отдельное состояние .

Иными словами, суффиксная ссылка

ведёт в предка вершины в суффиксном дереве, что и

требовалось доказать.

Доказательство теоремы 2.

Состояния суффиксного автомата соответствуют вершинам суффиксного дерева.

Рассмотрим произвольный переход

в суффиксном автомате

. Наличие этого перехода

 

означает, что — это такое состояние, класс эквивалентности которого содержит подстроку

.

В инвертированной строке это означает, что это такое состояние, которому соответствует подстрока, от которой (в тексте ) совпадает с от подстроки .

Это как раз и означает, что:

Первая часть теоремы доказана, осталось доказать вторую часть: что все сплошные переходы в автомате соответствуют суффиксным ссылкам в дереве. Сплошной переход отличается от несплошного тем,

что

, т.е. после приписывания символа мы попали в состояние со

строкой, максимальной из класса эквивалентности этого состояния. Это означает, что при вычислении

соответствующей расширяющей ссылки

мы сразу попали в вершину дерева, а не спускались

вниз до ближайшей вершины дерева. Таким образом, приписав один символ в начало, мы попали в другую вершину дерева — значит, если это и есть инвертированная суффиксная ссылка в дереве.

Теорема полностью доказана.

Теорема 3. Имея суффиксный автомат

, можно за время

построить суффиксное

дерево

.

 

 

 

Теорема 4. Имея суффиксное дерево

, можно за время

построить суффиксный

автомат

.

 

 

 

Доказательство теоремы 3.

Суффиксное дерево будет содержать столько же вершин, сколько состояний в , причём

вершине дерева, получившейся из состояния автомата, соответствует строка длины .

Согласно теореме 1, рёбра в дереве образуются как инвертированные суффиксные ссылки, и дуговые метки можно найти, исходя из разности состояний, и дополнительно зная для каждого состояния автомата один любой элемент его множества (этот один элемент множества можно поддерживать при построении автомата).

Суффиксные ссылки в дереве мы можем построить согласно теореме 2: для этого достаточно просмотреть все сплошные переходы в автомате, и для каждого такого перехода добавить ссылку .

Таким образом, за время

мы можем построить суффиксное дерево вместе с суффиксными ссылками в нём.

(Если мы считаем размер

алфавита не константой, то на всё перестроение потребуется время

.)

Доказательство теоремы 4.

 

 

Суффиксный автомат

будет содержать столько же состояний, сколько вершин в

. У

каждого состояния его длиннейшая строка

будет соответствовать инвертированному пути из

корня дерева до вершины .

 

 

Согласно теореме 2, чтобы построить все переходы в суффиксном автомате, нам надо найти все расширяющие ссылки .

Во-первых, заметим, что часть этих расширяющих ссылок получаются непосредственно из суффиксных ссылок в дереве. В самом деле, если для любой вершины мы рассмотрим её суффиксную ссылку , то это означает,

что надо провести расширяющую ссылку из в по первому символу строки, соответствующей вершине .

Однако так мы найдём не все расширяющие ссылки. Дополнительно надо пройтись по суффиксному дереву от листьев до корня, и для каждой вершины просмотреть всех её сыновей, для каждого сына просмотреть все расширяющие

ссылки

, и скопировать эту ссылку в вершину , если по этому символу ссылка из вершины ещё не

была найдена:

 

 

 

 

 

Этот процесс отработает за время , если мы считаем размер алфавита константным.

Наконец, осталось построить суффиксные ссылки в автомате, однако, согласно теореме 1, эти суффиксные ссылки получаются просто как рёбра суффиксного дерева .

Таким образом, описанный алгоритм за время

строит суффиксный автомат по суффиксному дереву

для инвертированной строки.

 

(Если же мы считаем, что размер алфавита — также переменная величина, то асимптотика увеличится до .)

Применения при решении задач

Ниже мы рассмотрим, какие задачи можно решать с помощью суффиксного автомата.

Мы для простоты будем считать размер алфавита константой, что позволит нам считать асимптотику построения суффиксного автомата и прохода по нему константными.

Проверка вхождения

Условие. Дан текст , и поступают запросы в виде: дана строка , требуется проверить, входит или нет строка в текст как подстрока.

Асимптотика. Препроцессинг и на один запрос. Решение. Построим суффиксный автомат по тексту за время .

Как теперь отвечать на один запрос. Пусть текущее состояние — это переменная , изначально она равна начальному состоянию . Будем идти по символам строки , соответствующим образом делая переход из текущего состояния в новое состояние. Если в какой-то момент случилось, что перехода из текущего состояния по нужному символу не оказалось — то ответ на запрос "нет". Если же мы смогли обработать всю строку , то ответ на запрос "да".

Понятно, что это будет работать за время

. Более того, алгоритм фактически ищет

длину наидлиннейшего префикса , встречающегося в тексте — и если входные образцы таковы, что эти длины маленькие, то и алгоритм будет работать значительно быстрее, не обрабатывая всю строку целиком.

Количество различных подстрок

Условие. Дана строка . Требуется узнать количество различных её подстрок.

Асимптотика. .

Решение. Построим суффиксный автомат по строке .

В суффиксном автомате любой подстроке строки соответствует какой-то путь в автомате. Поскольку повторяющихся строк в автомате быть не может, то ответ на задачу — это количество различных путей в автомате, начинающихся в начальной вершине .

Учитывая, что суффиксный автомат представляет собой ациклический граф, количество различных путей можно считать в нём с помощью динамического программирования.

А именно, пусть — это количество различных путей, начинающихся с состояния (включая путь длины ноль). Тогда верно:

т.е. можно выразить как сумму ответов по всевозможным переходам из состояния .

Ответом на задачу будет значение (единица отнимается, чтобы не учитывать пустую подстроку).

Суммарная длина различных подстрок

Условие. Дана строка . Требуется узнать суммарную длину всех различных её подстрок.

Асимптотика. .

Решение. Решение задачи аналогично предыдущей, только теперь надо считать в динамике две величины: количество различных подстрок и их суммарную длину .

Как считать , описано в предыдущей задаче, а величину можно вычислить таким образом:

 

 

т.е. мы берём ответ для каждой вершины , и прибавляем к нему

, тем самым как бы приписывая в начало

каждой из строк по одному символу.

 

Наименьший циклический сдвиг

Условие. Дана строка . Требуется найти лексикографически наименьший её циклический сдвиг.

Асимптотика.

.

 

Решение. Построим суффиксный автомат для строки

. Тогда этот автомат будет содержать в себе как пути

все циклические сдвиги строки .

 

Следовательно, задача сведётся к тому, чтобы найти в автомате лексикографически минимальный путь

длины

, что делается тривиальным образом: мы стартуем в начальном состоянии и каждый раз

действуем жадно, переходя по переходу с минимальным символом.

Количество вхождений

Условие. Дан текст , и поступают запросы в виде: дана строка , требуется узнать, сколько раз строка входит в текст как подстрока (вхождения могут перекрываться).

Асимптотика. Препроцессинг

и

на один запрос.

 

Решение. Построим суффиксный автомат по тексту .

 

 

Дальше нам надо сделать такой препроцессинг: для каждого состояния

автомата посчитать число

,

равное размеру множества

. В самом деле, все строки, соответствующие одному и тому же

 

состоянию, входят в одинаковое число раз, равное количеству позиций в множестве .

Однако явно поддерживать множества

для всех состояний мы не можем, поэтому научимся считать только

их размеры

.

 

Для этого поступим следующим образом. Для каждого состояния, если оно не было получено путём клонирования (и начальное состояние мы также не учитываем), изначально присвоим . Затем будем идти по

всем состояниям в порядке убывания их длины и пробрасывать текущее значение по суффиксной ссылке:

Утверждается, что в конце концов мы так посчитаем для каждого состояния правильные значения .

Почему это верно? Всего состояний, полученных не путём клонирования, ровно

, и -ое из них

появилось, когда мы добавили первые символов. Следовательно, каждому из этих состояний мы ставим в соответствие эту позицию, при обработке которой оно появилось. Поэтому изначально у каждого такого состояния , а у всех остальных состояний .

Затем мы выполняем для каждого такую операцию:

. Смысл этого заключается в

том, что если строка, соответствующая состоянию , встречалась

раз, то все её суффиксы будут

встречаться столько же.

 

Почему тем самым мы не учтём одну и ту же позицию несколько раз? Потому что из каждого состояния его значение "пробрасывается" только один раз, поэтому не могло так получиться, что из одного состояния его значение "пробросилось" до какого-то другого состояния дважды, двумя разными путями.

Таким образом, мы научились считать эти величины для всех состояний автомата.

После этого ответ на запрос тривиален — надо просто вернуть , где — состояние, соответствующее образцу .

Позиция первого вхождения

Условие. Дан текст , и поступают запросы в виде: дана строка , требуется узнать позицию начала первого вхождения строки .

Асимптотика. Препроцессинг и на один запрос.

Решение. Построим суффиксный автомат по тексту .

 

Для решения задачи нам также надо добавить в препроцессинг нахождение позиций

для всех

состояний автомата, т.е. для каждого состояния мы хотим найти позицию

окончания первого

вхождения. Иными словами, мы хотим найти заранее минимальный элемент каждого из множеств (поскольку явно поддерживать все множества мы не можем).

Поддерживать эти позиции

проще всего прямо по ходу построения автомата: когда мы создаём

новое состояние при входе в функцию , то выставляем ему:

 

 

 

 

(если мы работаем в

-индексации).

 

 

При клонировании вершины в

мы ставим:

 

 

 

 

 

(поскольку другой вариант значения только один — это

, что явно больше).

Таким образом, ответ на запрос — это просто

, где —

состояние, соответствующее образцу .

 

Позиции всех вхождений

 

Условие. Дан текст

, и поступают запросы в виде: дана строка , требуется вывести позиции всех её вхождений

в строку (вхождения могу перекрываться).

 

Асимптотика. Препроцессинг

 

. Ответ на один запрос за

 

 

, где

— это размер ответа, т.е. мы будем решать задачу за

время порядка размера ввода и вывода.

 

Решение. Построим суффиксный автомат по тексту . Аналогично предыдущей задаче, посчитаем в процессе построения автомата для каждого состояния позицию окончания первого вхождения.

Пусть теперь поступил запрос — строка . Найдём, какому состоянию она соответствует.

Понятно, что

точно должно входить в ответ. Какие ещё позиции надо найти? Мы учли состояние

автомата, содержащее строку , однако не учли другие состояния, которым соответствуют такие строки, что является их суффиксом.

Иными словами, нам требуется найти все состояния, из которых достижимо по суффиксным ссылкам состояние .

Следовательно, для решения задачи нам потребуется сохранить для каждого состояния список суффиксных ссылок, ведущих в него. Ответ на запрос тогда будет заключаться в том, чтобы сделать обход в глубину/ в ширину по этим инвертированным суффиксным ссылкам, начиная с состояния .

Этот обход будет работать за время

, поскольку мы не посетим одно и то же состояние

дважды (потому что из каждого состояния суффиксная ссылка выходит только одна, поэтому не может быть двух путей, ведущих в одно и то же состояние).

Правда, надо учитывать, что у двух состояний их значения могут совпадать: если одно состояние

было получено клонированием другого. Однако это не ухудшает асимптотику, поскольку у каждой неклонированной вершины может быть максимум один клон.

Более того, можно легко избавиться от вывода повторяющихся позиций, если мы не будем добавлять в ответ от состояний-клонов. В самом деле, в любое состояние-клон ведёт суффиксная ссылка из

того первоначального состояния, которое это состояние клонировало. Таким образом, если мы для каждого состояния запомним флаг , и не будем добавлять в ответ от состояний, для

которых , то мы тем самым получим все требуемые позиций без повторов. Приведём наброски реализации:

struct state {

...

bool is_clon; int first_pos;

vector<int> inv_link;

};

... после построения автомата ...

for (int v=1; v<sz; ++v)

st[st[v].link].inv_link.push_back (v);

...

// ответ на запрос - вывод всех вхождений (возможно, с повторами) void output_all_occurences (int v, int P_length) {

if (! st[v].is_clon)

cout << st[v].first_pos - P_length + 1 << endl; for (size_t i=0; i<st[v].inv_link.size(); ++i)

output_all_occurences (st[v].inv_link[i], P_length);

}

Поиск кратчайшей строки, не входящей в данную

Условие. Дана строка , и задан определённый алфавит. Требуется найти такую строку наименьшей длины, что она не встречается в как подстрока.

Асимптотика. Решение за .

Решение. Решать будет динамическим программирование по автомату, построенному для строки .

Пусть — это ответ для вершины , т.е. мы уже набрали часть подстроки, оказавшись в состоянии , и хотим

найти наименьшее число символов, которое надо ещё добавить, чтобы выйти за пределы автомата, найдя несуществующий переход.

Считается

очень просто. Если из нет перехода хотя бы по одному символу из алфавита, то

: мы

можем приписать такой символ и выйти за пределы автомата, получив тем самым искомую строку.

В противном случае, одним символом обойтись не получится, поэтому надо взять минимум из ответов по всевозможным символам:

Ответ на задачу будет равен , а саму строку можно восстановить, восстановив, каким образом в динамике получился этот минимум.

Наидлиннейшая общая подстрока двух строк

Условие. Даны две строки и . Требуется найти их наидлиннейшую общую подстроку, т.е. такую строку , что она является подстрокой и , и .

Асимптотика. Решение за .

Решение. Построим суффиксный автомат по строке .

Будем теперь идти по строке , и для каждого префикса искать наидлиннейший суффикс этого префикса, встречающийся в . Иными словами, мы для каждой позиции в строке хотим найти наидлиннейшую общую подстроку и , заканчивающуюся именно в этой позиции.

Для этого будем поддерживать две переменные: текущее состояние и текущую длину . Эти

две переменные будут описывать текущую совпадающую часть: её длину и состояние, которое соответствует ей

(без хранения длины нельзя обойтись, поскольку одному состоянию может соответствовать сразу несколько строк разной длины).

Изначально , , т.е. совпадение пустое.

Пусть теперь мы рассматриваем символ и хотим пересчитать ответ для него.

Если из состояния в автомате есть переход по символу

, то мы просто совершаем этот переход и увеличиваем

на единицу.

 

Если же из состояния нет требуемого перехода, то мы должны попытаться укоротить текущую совпадающую часть, для чего надо перейти по суффиксной ссылке:

При этом текущую длину надо укоротить, но оставить максимально возможной. Очевидно, для этого надо присвоить , поскольку после прохода по суффиксной ссылке нас удовлетворит подстрока любой

длины, соответствующая этому состоянию:

Если из нового состояния вновь не будет перехода по требуемому символу, то мы снова должны пройти по суффиксной ссылке и уменьшить , и так далее, пока не найдём переход (тогда перейдём к пункту 1) или мы не попадём

в фиктивное состояние

(что означает, что символ

вообще не встречается в , поэтому

присваиваем

и переходим к следующему

).

Ответом на задачу будет максимум из значений за всё время обхода.

Асимптотика такого прохода составляет

, поскольку за один ход мы можем либо увеличить на

единицу , либо сделать несколько проходов по суффиксной ссылке, каждый из которых будет строго уменьшать значение . Следовательно, уменьшений не могло быть больше , что и означает линейную асимптотику.

Реализация:

string lcs (string s, string t) { sa_init();

for (int i=0; i<(int)s.length(); ++i) sa_extend (s[i]);

int v = 0, l = 0,

best = 0, bestpos = 0;

for (int i=0; i<(int)t.length(); ++i) {

while (v && ! st[v].next.count(t[i])) { v = st[v].link;

l = st[v].length;

}

if (st[v].next.count(t[i])) { v = st[v].next[t[i]]; ++l;

}

if (l > best)

best = l, bestpos = i;

}

return t.substr (bestpos-best+1, best);

}

Наибольшая общая подстрока нескольких строк.

Условие. Даны

строк . Требуется найти их наидлиннейшую общую подстроку, т.е. такую строку , что

она является подстрокой всех .

 

Асимптотика. Решение за

.

Решение. Склеим все строки

в одну строку , приписав после каждой строки свой собственный

символ-разделитель

(т.е. введя

дополнительных спец. символов ):

 

 

 

 

 

 

Построим для строки суффиксный автомат.

Теперь нам требуется найти такую строку в автомате, которая содержится во всех строках , и в этом нам помогут добавленные спец. символы. Заметим, что если какая-либо подстрока входит в некоторую строку , то

в суффиксном автомате из этой подстроки найдётся путь, содержащий символ

, и не содержащий остальных

 

символов

.

 

 

 

Таким образом, нам требуется посчитать достижимости: для каждого состояния автомата и каждого символа

есть

ли путь, содержащий разделитель

, и не содержащий других разделителей. Это легко сделать обходом в

 

глубину/ширину или ленивой динамикой. После этого ответом на задачу будет строка

для состояния ,

из которого были найдены пути по всем символам.

Литература

Приведём сначала список первых работ, связанных с суффиксными автоматами:

A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler, R. McConnell. Linear Size Finite Automata for the Set of All Subwords of a Word. An Outline of Results [1983]

A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler. The Smallest Automaton Recognizing the Subwords of a Text [1984]

Maxime Crochemore. Optimal Factor Transducers [1985]

Maxime Crochemore. Transducers and Repetitions [1986]

A. Nerode. Linear automaton transformations [1958]

Помимо этого, в более современных источниках эта тема затрагивается во многих книгах по строковым алгоритмам:

Maxime Crochemore, Wowjcieh Rytter. Jewels of Stringology [2002]

Bill Smyth. Computing Patterns in Strings [2003]

Билл Смит. Методы и алгоритмы вычислений на строках [2006]

Нахождение всех подпалиндромов

Постановка задачи

Дана строка длины . Требуется найти все такие пары

, где

, что подстрока

является палиндромом (т.е. читается одинаково слева направо и справа налево).

Уточнение постановки

Понятно, что в худшем случае таких подстрок-палиндромов может быть

, и на первый взгляд кажется,

что алгоритма с линейной асимптотикой существовать не может.

 

Однако информацию о найденных палиндромах можно возвращать более компактно: для каждой

позиции

найдём значения

и

, обозначающие количество палиндромов

соответственно нечётной и чётной длины с центром в позиции .

Например, в строке

есть три палиндрома нечётной длины с центром в символе

, т.е.

значение

:

 

 

 

 

 

 

 

А в строке есть два палиндрома чётной длины с центром в символе , т.е. значение :

Т.е. идея — в том, что если есть подпалиндром длины с центром в какой-то позиции , то есть также

 

подпалиндромы длины

,

, и т.д. с центрами в . Поэтому двух таких массивов

и

достаточно

для хранения информации обо всех подпалиндромах этой строки.

Достаточно неожиданным фактом является то, что существует довольно простой алгоритм, который вычисляет эти "массивы палиндромностей" и за линейное время. Этот алгоритм и описывается в данной статье.

Решение

Вообще говоря, данная задача имеет несколько известных решений: с помощью техники хэширования её можно решить за , а с помощью суффиксных деревьев и быстрого алгоритма LCA эту задачу можно решить за .

Однако описываемый в данной статье метод значительно проще, и обладает меньшими скрытыми константами

васимптотике времени и памяти. Этот алгоритм был открыт Гленном Манакером (Glenn Manacher)

в1975 г.

Тривиальный алгоритм

Во избежание неоднозначностей при дальнейшем описании условимся, что же такое есть "тривиальный алгоритм".

Это алгоритм, который для поиска ответа в позиции раз за разом пробует увеличить ответ на единицу, каждый раз сравнивая пару соответствующих символов.

Такой алгоритм слишком медленен, весь ответ он может посчитать лишь за время . Приведём для наглядности его реализацию:

vector<int> d1 (n), d2 (n); for (int i=0; i<n; ++i) {

d1[i] = 1;

while (i-d1[i] >= 0 && i+d1[i] < n && s[i-d1[i]] == s[i+d1[i]]) ++d1[i];

d2[i] = 0;

while (i-d2[i]-1 >= 0 && i+d2[i] < n && s[i-d2[i]-1] == s[i+d2[i]]) ++d2[i];

}

Алгоритм Манакера

Научимся сначала находить все подпалиндромы нечётной длины, т.е. вычислять массив

; решение для

палиндромов чётной длины (т.е. нахождение массива ) получится небольшой модификацией этого.

Для быстрого вычисления будем поддерживать границы

самого правого из обнаруженных подпалиндрома (т.

е. подпалиндрома с наибольшим значением ). Изначально можно положить .

Итак, пусть мы хотим вычислить значение

для очередного , при этом все предыдущие значения

уже подсчитаны.

 

Если не находится в пределах текущего подпалиндрома, т.е. , то просто выполним тривиальный алгоритм.

 

Т.е. будем последовательно увеличивать значение

, и проверять каждый раз — правда ли текущая

 

подстрока

 

является палиндромом. Когда мы найдём первое расхождение, либо когда мы

 

дойдём до границ строки — останавливаемся: мы окончательно посчитали значение

. После этого мы должны

 

не забыть обновить значения

 

.

 

 

 

 

Рассмотрим теперь случай, когда

.

 

 

 

 

 

Попробуем извлечь часть информации из уже подсчитанных значений

. А именно, отразим позицию

внутри подпалиндрома

, т.е. получим позицию

 

, и рассмотрим значение

. Поскольку

— позиция, симметричная позиции

, то почти всегда мы можем просто присвоить

 

.

Иллюстрация этого отражения (палиндром вокруг фактически "копируется" в палиндром вокруг

):

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Однако здесь есть тонкость, которую надо обработать правильно: когда "внутренний палиндром" достигает

границы внешнего или вылазит за неё, т.е.

(или, что то же самое,

просто присвоить

). Поскольку за границами внешнего палиндрома никакой симметрии не гарантируется, то

будет уже некорректно: у нас недостаточно сведений, чтобы утверждать, что в

позиции подпалиндром имеет такую же длину.

На самом деле, чтобы правильно обрабатывать такие ситуации, надо "обрезать" длину подпалиндрома, т.е. присвоить . После этого следует пустить тривиальный алгоритм, который будет пытаться

увеличить значение , пока это возможно.

Иллюстрация этого случая (на ней палиндром с центром в изображён уже "обрезанным" до такой длины, что он впритык помещается во внешний палиндром):

(На этой иллюстрации показано, что, хотя палиндром с центром в позиции мог быть и более длинным, выходящим за пределы внешнего палиндрома, — но в позиции мы можем использовать только ту его часть, которая целиком помещается во внешний палиндром. Но ответ для позиции может быть больше, чем эта часть, поэтому дальше мы должны запустить тривиальный поиск, который будет пытаться раздвинуть его за пределы

внешнего палиндрома, т.е. в область "try moving here".)

В завершение описания алгоритма сталось только напомнить, что надо не забывать обновлять значения

 

после вычисления очередного значения

.

 

Также повторимся, что выше мы описали рассуждения для вычисления массива нечётных палиндромов

; для

массива чётных палиндромов

все рассуждения аналогичны.

 

Оценка асимптотики алгоритма Манакера

На первый взгляд не очевидно, что данный алгоритм имеет линейную асимптотику: при вычислении ответа для определённой позиции в нём нередко запускается тривиальный алгоритм поиска палиндромов.

Однако более внимательный анализ показывает, что алгоритм всё же линеен. (Стоит сослаться на известный

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]