Алгоритм Манакера — различия между версиями
Строка 24: | Строка 24: | ||
1. <tex>i > r</tex>, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции <tex>i</tex>. | 1. <tex>i > r</tex>, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции <tex>i</tex>. | ||
− | 2. <tex>i \leq r</tex>. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома <tex>[l;r] : j = (r - i) + l</tex>. Поскольку <tex>i</tex> и <tex>j</tex> - симметричные позиции, то мы можем утверждать, <tex>d1[i] = d1[j]</tex>. Однако надо не забыть про один граничный случай: что если <tex>i + d1[j] - 1</tex> выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение <tex>d1[i]</tex> следующим образом: <tex>d1[i] = min(r - i, d1[j]). После этого запустим наивный алгоритм, который будет увеличивать значение <tex>d1[i]</tex>, пока это возможно. | + | 2. <tex>i \leq r</tex>. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома <tex>[l;r] : j = (r - i) + l</tex>. Поскольку <tex>i</tex> и <tex>j</tex> - симметричные позиции, то мы можем утверждать, <tex>d1[i] = d1[j]</tex>. Однако надо не забыть про один граничный случай: что если <tex>i + d1[j] - 1</tex> выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение <tex>d1[i]</tex> следующим образом: <tex>d1[i] = min(r - i, d1[j])</tex>. После этого запустим наивный алгоритм, который будет увеличивать значение <tex>d1[i]</tex>, пока это возможно. |
+ | |||
+ | После каждого шага важно не забывать обновлять значения <tex>[l;r]</tex> | ||
Заметим, что массив <tex>d2</tex> считается аналогичным образом, нужно лишь немного изменить индексы. | Заметим, что массив <tex>d2</tex> считается аналогичным образом, нужно лишь немного изменить индексы. | ||
===Псевдокод=== | ===Псевдокод=== | ||
− | Приведем код, который вычисляет значения | + | Приведем код, который вычисляет значения массива <tex>d1</tex>: |
+ | |||
+ | <font color=green>// <tex>s</tex> {{---}} исходная строка</font> | ||
+ | '''int''' l = 0 | ||
+ | '''int''' r = -1 | ||
+ | '''for''' i = 1 '''to''' n | ||
+ | '''int''' k = 0 | ||
+ | '''if''' i <= r | ||
+ | k = min(r - i, d[r - i + l]) | ||
+ | '''while''' i + k + 1 <= n '''and''' i - k - 1 > 0 '''and''' s[i + k + 1] == s[i - k - 1] | ||
+ | k++ | ||
+ | d1[i] = k | ||
+ | '''if''' i + k > r | ||
+ | l = i - k | ||
+ | r = i + k | ||
+ | Вычисление значений массива <tex>d2</tex>: | ||
<font color=green>// <tex>s</tex> {{---}} исходная строка</font> | <font color=green>// <tex>s</tex> {{---}} исходная строка</font> | ||
− | |||
'''int''' l = 0 | '''int''' l = 0 | ||
'''int''' r = -1 | '''int''' r = -1 | ||
'''for''' i = 1 '''to''' n | '''for''' i = 1 '''to''' n | ||
− | '''int''' k = | + | '''int''' k = 0 |
'''if''' i <= r | '''if''' i <= r | ||
− | k = | + | k = min(r - i + 1, d[r - i + l + 1]) |
− | '''while''' i + k <= n '''and''' i - k > 0 '''and''' s[i + k] == s[i - k] | + | '''while''' i + k <= n '''and''' i - k - 1 > 0 '''and''' s[i + k] == s[i - k - 1] |
k++ | k++ | ||
− | + | d2[i] = k | |
− | + | '''if''' i + k - 1 > r | |
− | + | l = i - k | |
− | + | r = i + k - 1 |
Версия 22:39, 10 марта 2016
Задача: |
Пусть дана строка | . Требуется найти - длина наибольшего палиндрома нечетной длины с центром в позиции и - аналогично для палиндромов четной длины для всех от 1 до .
Наивный алгоритм
Идея
Опишем сначала наивный алгоритм решения задачи. Чтобы посчитать ответ для позиции
, будем на каждом шаге увеличивать длину палиндрома с центром в и убеждаться, что рассматриваемая строка не перестала быть палиндромом, либо не произошел выход за границы массива. Очевидно, что такой алгоритм будет работать заПсевдокод
//— исходная строка // — массивы для записи ответа for i = 1 to n d1[i] = 1 while i - d1[i] > 0 and i + d1[i] <= n and s[i - d1[i]] == s[i + d1[i]] d1[i]++ d2[i] = 0 while i - d2[i] - 1 > 0 and i + d2[i] <= n and s[i - d2[i] - 1] == s[i + d2[i]] d2[i]++
Алгоритм Манакера
Идея
Алгоритм, который будет описан далее, отличается от наивного тем, что использует значения, посчитанные ранее. Будем поддерживать границы самого правого из найденных палиндромов -
. Итак, пусть мы хотим вычислить - т.е. длину наибольшего палиндрома с центром в позиции . При этом все предыдущие значения в массиве уже посчитаны. Возможны два случая:1.
, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции .2.
. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома . Поскольку и - симметричные позиции, то мы можем утверждать, . Однако надо не забыть про один граничный случай: что если выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет, то необходимо ограничить значение следующим образом: . После этого запустим наивный алгоритм, который будет увеличивать значение , пока это возможно.После каждого шага важно не забывать обновлять значения
Заметим, что массив
считается аналогичным образом, нужно лишь немного изменить индексы.Псевдокод
Приведем код, который вычисляет значения массива
://
— исходная строка
int l = 0
int r = -1
for i = 1 to n
int k = 0
if i <= r
k = min(r - i, d[r - i + l])
while i + k + 1 <= n and i - k - 1 > 0 and s[i + k + 1] == s[i - k - 1]
k++
d1[i] = k
if i + k > r
l = i - k
r = i + k
Вычисление значений массива
://
— исходная строка
int l = 0
int r = -1
for i = 1 to n
int k = 0
if i <= r
k = min(r - i + 1, d[r - i + l + 1])
while i + k <= n and i - k - 1 > 0 and s[i + k] == s[i - k - 1]
k++
d2[i] = k
if i + k - 1 > r
l = i - k
r = i + k - 1