<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=46.237.5.186&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=46.237.5.186&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/46.237.5.186"/>
		<updated>2026-04-08T22:53:12Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BD%D0%B0%D0%BA%D0%B5%D1%80%D0%B0&amp;diff=75040</id>
		<title>Алгоритм Манакера</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BD%D0%B0%D0%BA%D0%B5%D1%80%D0%B0&amp;diff=75040"/>
				<updated>2020-09-18T09:16:23Z</updated>
		
		<summary type="html">&lt;p&gt;46.237.5.186: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дана строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Требуется найти количество подстрок &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, являющиеся палиндромами. Более формально, все такие пары &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;s[i \ldots j]&amp;lt;/tex&amp;gt; — [[Основные_определения,_связанные_со_строками#palindrome | палиндром]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Уточнение постановки==&lt;br /&gt;
Легко увидеть, что таких подстрок в худшем случае будет &amp;lt;tex&amp;gt;n^2&amp;lt;/tex&amp;gt;. Значит, нужно найти компактный способ хранения информации о них. Пусть &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; — количество палиндромов нечётной длины с центром в позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;d_2[i]&amp;lt;/tex&amp;gt; — аналогичная величина для палиндромов чётной длины. Далее научимся вычислять значения этих массивов.&lt;br /&gt;
&lt;br /&gt;
== Наивный алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Рассмотрим сначала задачу поиска палиндромов нечётной длины. Центром строки нечётной длины назовём символ под индексом &amp;lt;tex&amp;gt;\left\lfloor \dfrac{|t|}{2}\right\rfloor&amp;lt;/tex&amp;gt;. Для каждой позиции в строке &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; найдем длину наибольшего палиндрома с центром в этой позиции. Очевидно, что если строка &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; является палиндромом, то строка полученная вычеркиванием первого и последнего символа из &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; также является палиндромом, поэтому длину палиндрома можно искать [[Целочисленный_двоичный_поиск | бинарным поиском]]. Проверить совпадение левой и правой половины можно выполнить за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, используя метод хеширования. &lt;br /&gt;
&lt;br /&gt;
Для палиндромов чётной длины алгоритм такой же. Центр строки чётной длины {{---}} некий мнимый элемент между &amp;lt;tex&amp;gt;\dfrac{|t|}{2} - 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\dfrac{|t|}{2}&amp;lt;/tex&amp;gt;. Только требуется проверять вторую строку со сдвигом на единицу. Следует заметить, что мы не посчитаем никакой палиндром дважды из-за четности-нечетности длин палиндромов.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
 '''int''' binarySearch(s : '''string''', center, shift : '''int'''):&lt;br /&gt;
     ''&amp;lt;font color=green&amp;gt;//shift = 0 при поиске палиндрома нечётной длины, иначе shift = 1&amp;lt;/font&amp;gt;''&lt;br /&gt;
     '''int''' l = -1, r = min(center, s.length - center + shift), m = 0&lt;br /&gt;
     '''while''' r - l != 1&lt;br /&gt;
         m = l + (r - l) / 2&lt;br /&gt;
         ''&amp;lt;font color=green&amp;gt;//reversed_hash возвращает хэш развернутой строки s&amp;lt;/font&amp;gt;''&lt;br /&gt;
         '''if''' hash(s[center - m..center]) == reversed_hash(s[center + shift..center + shift + m])&lt;br /&gt;
             l = m&lt;br /&gt;
         '''else'''&lt;br /&gt;
             r = m&lt;br /&gt;
     '''return''' r&lt;br /&gt;
&lt;br /&gt;
 '''int''' palindromesCount(s : '''string'''):&lt;br /&gt;
     '''int''' ans = 0&lt;br /&gt;
     '''for''' i = 0 '''to''' s.length&lt;br /&gt;
         ans += binarySearch(s, i, 0) + binarySearch(s, i, 1)&lt;br /&gt;
     '''return''' ans&lt;br /&gt;
&lt;br /&gt;
=== Время работы ===&lt;br /&gt;
Изначальный подсчет хешей производится за &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt;. Каждая итерация будет выполняться за &amp;lt;tex&amp;gt;O(\log(|s|))&amp;lt;/tex&amp;gt;, всего итераций {{---}} &amp;lt;tex&amp;gt;|s|&amp;lt;/tex&amp;gt;. Итоговое время работы алгоритма &amp;lt;tex&amp;gt;O(|s|+|s|\cdot \log(|s|)) = O(|s|\cdot \log(|s|))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Избавление от коллизий ===&lt;br /&gt;
У хешей есть один недостаток {{---}} коллизии: можно подобрать входные данные так, что хеши разных строк будут совпадать. Абсолютно точно проверить две подстроки на совпадение можно с помощью [[Суффиксный массив | суффиксного массива]], но с дополнительной памятью &amp;lt;tex&amp;gt;O(|s|\cdot \log(|s|))&amp;lt;/tex&amp;gt;. Для этого построим суффиксный массив для строки &amp;lt;tex&amp;gt;s + \# + reverse(s)&amp;lt;/tex&amp;gt;, при этом сохраним промежуточные результаты классов эквивалентности &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt;. Пусть нам требуется проверить на совпадение подстроки &amp;lt;tex&amp;gt;s[i \ldots i + l]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s[j \ldots j + l]&amp;lt;/tex&amp;gt;. Разобьем каждую нашу строку на две пересекающиеся подстроки длиной &amp;lt;tex&amp;gt;2^k&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;k = \lfloor \log{l} \rfloor&amp;lt;/tex&amp;gt;. Тогда наши строки совпадают, если &amp;lt;tex&amp;gt;c[k][i] = c[k][j]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;c[k][i + l - 2^k] = c[k][j + l - 2^k]&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма: предподсчет за построение суффиксного массива и &amp;lt;tex&amp;gt;O(\log(|s|))&amp;lt;/tex&amp;gt; на запрос, если предподсчитать все &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм Манакера==&lt;br /&gt;
===Идея===&lt;br /&gt;
Алгоритм, который будет описан далее, отличается от наивного тем, что использует значения, посчитанные ранее. &lt;br /&gt;
Будем поддерживать границы самого правого из найденных палиндромов — &amp;lt;tex&amp;gt;[l; r]&amp;lt;/tex&amp;gt;. Итак, пусть мы хотим вычислить &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; — т.е. длину наибольшего палиндрома с центром в позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. При этом все предыдущие значения в массиве &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; уже посчитаны. Возможны два случая:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tex&amp;gt;i &amp;gt; r&amp;lt;/tex&amp;gt;, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# &amp;lt;tex&amp;gt;i \leqslant r&amp;lt;/tex&amp;gt;. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома &amp;lt;tex&amp;gt;[l;r] : j = (r - i) + l&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; — симметричные позиции, то если &amp;lt;tex&amp;gt;d_1[j] = k&amp;lt;/tex&amp;gt;, мы можем утверждать, что и &amp;lt;tex&amp;gt;d_1[i] = k&amp;lt;/tex&amp;gt;. Это объясняется тем, что палиндром симметричен относительно своей центральной позиции. Т.е. если имеем некоторый палиндром длины &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; с центром в позиции &amp;lt;tex&amp;gt;l \leqslant i \leqslant r&amp;lt;/tex&amp;gt;, то в позиции &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, симметричной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; относительно отрезка &amp;lt;tex&amp;gt;[l; r]&amp;lt;/tex&amp;gt; тоже может находиться палиндром длины &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.  Это можно лучше понять, посмотрев на рисунок. Снизу фигурными скобками обозначены равные подстроки. Однако стоит не забыть про один граничный случай: что если &amp;lt;tex&amp;gt;i + d_1[j] - 1&amp;lt;/tex&amp;gt; выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет (а значит мы не можем утверждать, что симметрия сохраняется), то необходимо ограничить значение &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; следующим образом: &amp;lt;tex&amp;gt;d_1[i] = \min(r - i, d_1[j])&amp;lt;/tex&amp;gt;. После этого запустим наивный алгоритм, который будет увеличивать значение &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt;, пока это возможно.&lt;br /&gt;
&lt;br /&gt;
После каждого шага важно не забывать обновлять значения &amp;lt;tex&amp;gt;[l;r]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt; считается аналогичным образом, нужно лишь немного изменить индексы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Манакер.png]]&lt;br /&gt;
&lt;br /&gt;
===Псевдокод===&lt;br /&gt;
Приведем код, который вычисляет значения массива &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} исходная строка&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''int[]''' calculate1('''string''' s):&lt;br /&gt;
   '''int''' l = 0&lt;br /&gt;
   '''int''' r = -1&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
     '''int''' k = 0&lt;br /&gt;
     '''if''' i &amp;lt;= r&lt;br /&gt;
        k = min(r - i, &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;[r - i + l])&lt;br /&gt;
     '''while''' i + k + 1 &amp;lt;= n '''and''' i - k - 1 &amp;gt; 0 '''and''' s[i + k + 1] == s[i - k - 1]&lt;br /&gt;
        k++&lt;br /&gt;
      &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;[i] = k&lt;br /&gt;
      '''if''' i + k &amp;gt; r&lt;br /&gt;
        l = i - k&lt;br /&gt;
        r = i + k&lt;br /&gt;
   '''return''' &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вычисление значений массива &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} исходная строка&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''int[]''' calculate2('''string''' s):&lt;br /&gt;
   '''int''' l = 0&lt;br /&gt;
   '''int''' r = -1&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
     '''int''' k = 0&lt;br /&gt;
     '''if''' i &amp;lt;= r&lt;br /&gt;
        k = min(r - i + 1, &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;[r - i + l + 1])&lt;br /&gt;
     '''while''' i + k &amp;lt;= n '''and''' i - k - 1 &amp;gt; 0 '''and''' s[i + k] == s[i - k - 1]&lt;br /&gt;
        k++&lt;br /&gt;
      &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;[i] = k&lt;br /&gt;
      '''if''' i + k - 1 &amp;gt; r&lt;br /&gt;
        l = i - k&lt;br /&gt;
        r = i + k - 1&lt;br /&gt;
   '''return''' &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Оценка сложности===&lt;br /&gt;
Внешний цикл в приведенном алгоритме выполняется ровно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — длина строки. Попытаемся понять, сколько раз будет выполнен внутренний цикл, ответственный за наивный подсчет значений. Заметим, что каждая итерация вложенного цикла приводит к увеличению &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Действительно, возможны следующие случаи:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tex&amp;gt;i &amp;gt; r&amp;lt;/tex&amp;gt;, т.е. сразу будет запущен наивный алгоритм и каждая его итерация будет увеличивать значение &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; хотя бы на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# &amp;lt;tex&amp;gt;i \leqslant r&amp;lt;/tex&amp;gt;. Здесь опять два случая:&lt;br /&gt;
## &amp;lt;tex&amp;gt;i + d[j] - 1 \leqslant r&amp;lt;/tex&amp;gt;, но тогда, очевидно, ни одной итерации вложенного цикла выполнено не будет.&lt;br /&gt;
## &amp;lt;tex&amp;gt;i + d[j] - 1 &amp;gt; r&amp;lt;/tex&amp;gt;,  тогда каждая итерация вложенного цикла приведет к увеличению &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; хотя бы на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Т.к. значение &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; не может увеличиваться более &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз, то описанный выше алгоритм работает за время &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Префикс-функция]]&lt;br /&gt;
* [[Z-функция]]&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [[Поиск наибольшей общей подстроки двух строк с использованием хеширования]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://e-maxx.ru/algo/palindromes_count MAXimal :: algo :: Нахождение всех подпалиндромов]&lt;br /&gt;
* [[wikipedia:ru:Поиск_длиннейшей_подстроки-палиндрома| Википедия — Поиск длиннейшей подстроки-палиндрома]]&lt;br /&gt;
* [https://habrahabr.ru/post/276195/ Алгоритмы для поиска палиндромов — Хабр]&lt;br /&gt;
* [http://e-maxx.ru/algo/suffix_array#5 MAXimal :: algo :: Суффиксный массив]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Основные определения. Простые комбинаторные свойства слов]]&lt;/div&gt;</summary>
		<author><name>46.237.5.186</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BD%D0%B0%D0%BA%D0%B5%D1%80%D0%B0&amp;diff=75039</id>
		<title>Алгоритм Манакера</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BD%D0%B0%D0%BA%D0%B5%D1%80%D0%B0&amp;diff=75039"/>
				<updated>2020-09-18T09:15:07Z</updated>
		
		<summary type="html">&lt;p&gt;46.237.5.186: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дана строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Требуется найти количество подстрок &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, являющиеся палиндромами. Более формально, все такие пары &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;s[i \ldots j]&amp;lt;/tex&amp;gt; — [[Основные_определения,_связанные_со_строками#palindrome | палиндром]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Уточнение постановки==&lt;br /&gt;
Легко увидеть, что таких подстрок в худшем случае будет &amp;lt;tex&amp;gt;n^2&amp;lt;/tex&amp;gt;. Значит, нужно найти компактный способ хранения информации о них. Пусть &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; — количество палиндромов нечётной длины с центром в позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;d_2[i]&amp;lt;/tex&amp;gt; — аналогичная величина для палиндромов чётной длины. Далее научимся вычислять значения этих массивов.&lt;br /&gt;
&lt;br /&gt;
== Наивный алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Рассмотрим сначала задачу поиска палиндромов нечётной длины. Центром строки нечётной длины назовём символ под индексом &amp;lt;tex&amp;gt;\left\lfloor \dfrac{|t|}{2}\right\rfloor&amp;lt;/tex&amp;gt;. Для каждой позиции в строке &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; найдем длину наибольшего палиндрома с центром в этой позиции. Очевидно, что если строка &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; является палиндромом, то строка полученная вычеркиванием первого и последнего символа из &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; также является палиндромом, поэтому длину палиндрома можно искать [[Целочисленный_двоичный_поиск | бинарным поиском]]. Проверить совпадение левой и правой половины можно выполнить за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, используя метод хеширования. &lt;br /&gt;
&lt;br /&gt;
Для палиндромов чётной длины алгоритм такой же. Центр строки чётной длины {{---}} некий мнимый элемент между &amp;lt;tex&amp;gt;\dfrac{|t|}{2} - 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\dfrac{|t|}{2}&amp;lt;/tex&amp;gt;. Только требуется проверять вторую строку со сдвигом на единицу. Следует заметить, что мы не посчитаем никакой палиндром дважды из-за четности-нечетности длин палиндромов.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
 '''int''' binarySearch(s : '''string''', center, shift : '''int'''):&lt;br /&gt;
     ''&amp;lt;font color=green&amp;gt;//shift = 0 при поиске палиндрома нечётной длины, иначе shift = 1&amp;lt;/font&amp;gt;''&lt;br /&gt;
     '''int''' l = -1, r = min(center, s.length - center + shift), m = 0&lt;br /&gt;
     '''while''' r - l != 1&lt;br /&gt;
         m = l + (r - l) / 2&lt;br /&gt;
         ''&amp;lt;font color=green&amp;gt;//reversed_hash возвращает хэш развернутой строки s&amp;lt;/font&amp;gt;''&lt;br /&gt;
         '''if''' hash(s[center - m..center]) == reversed_hash(s[center + shift..center + shift + m])&lt;br /&gt;
             l = m&lt;br /&gt;
         '''else'''&lt;br /&gt;
             r = m&lt;br /&gt;
     '''return''' r&lt;br /&gt;
&lt;br /&gt;
 '''int''' palindromesCount(s : '''string'''):&lt;br /&gt;
     '''int''' ans = 0&lt;br /&gt;
     '''for''' i = 0 '''to''' s.length&lt;br /&gt;
         ans += binarySearch(s, i, 0) + binarySearch(s, i, 1)&lt;br /&gt;
     '''return''' ans&lt;br /&gt;
&lt;br /&gt;
=== Время работы ===&lt;br /&gt;
Изначальный подсчет хешей производится за &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt;. Каждая итерация будет выполняться за &amp;lt;tex&amp;gt;O(\log(|s|))&amp;lt;/tex&amp;gt;, всего итераций {{---}} &amp;lt;tex&amp;gt;|s|&amp;lt;/tex&amp;gt;. Итоговое время работы алгоритма &amp;lt;tex&amp;gt;O(|s|+|s|\cdot \log(|s|)) = O(|s|\cdot \log(|s|))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Избавление от коллизий ===&lt;br /&gt;
У хешей есть один недостаток {{---}} коллизии: можно подобрать входные данные так, что хеши разных строк будут совпадать. Абсолютно точно проверить две подстроки на совпадение можно с помощью [[Суффиксный массив | суффиксного массива]], но с дополнительной памятью &amp;lt;tex&amp;gt;O(|s|\cdot \log(|s|))&amp;lt;/tex&amp;gt;. Для этого построим суффиксный массив для строки &amp;lt;tex&amp;gt;s + \# + reverse(s)&amp;lt;/tex&amp;gt;, при этом сохраним промежуточные результаты классов эквивалентности &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt;. Пусть нам требуется проверить на совпадение подстроки &amp;lt;tex&amp;gt;s[i \ldots i + l]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s[j \ldots j + l]&amp;lt;/tex&amp;gt;. Разобьем каждую нашу строку на две пересекающиеся подстроки длиной &amp;lt;tex&amp;gt;2^k&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;k = \lfloor \log{l} \rfloor&amp;lt;/tex&amp;gt;. Тогда наши строки совпадают, если &amp;lt;tex&amp;gt;c[k][i] = c[k][j]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;c[k][i + l - 2^k] = c[k][j + l - 2^k]&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма: предподсчет за построение суффиксного массива и &amp;lt;tex&amp;gt;O(\log(|s|))&amp;lt;/tex&amp;gt; на запрос, если предподсчитать все &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм Манакера==&lt;br /&gt;
===Идея===&lt;br /&gt;
Алгоритм, который будет описан далее, отличается от наивного тем, что использует значения, посчитанные ранее. &lt;br /&gt;
Будем поддерживать границы самого правого из найденных палиндромов — &amp;lt;tex&amp;gt;[l; r]&amp;lt;/tex&amp;gt;. Итак, пусть мы хотим вычислить &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; — т.е. длину наибольшего палиндрома с центром в позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. При этом все предыдущие значения в массиве &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; уже посчитаны. Возможны два случая:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tex&amp;gt;i &amp;gt; r&amp;lt;/tex&amp;gt;, т.е. текущая позиция не попадает в границы самого правого из найденных палиндромов. Тогда просто запустим наивный алгоритм для позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# &amp;lt;tex&amp;gt;i \leqslant r&amp;lt;/tex&amp;gt;. Тогда попробуем воспользоваться значениями, посчитанным ранее. Отразим нашу текущую позицию внутри палиндрома &amp;lt;tex&amp;gt;[l;r] : j = (r - i) + l&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; — симметричные позиции, то если &amp;lt;tex&amp;gt;d_1[j] = k&amp;lt;/tex&amp;gt;, мы можем утверждать, что и &amp;lt;tex&amp;gt;d_1[i] = k&amp;lt;/tex&amp;gt;. Это объясняется тем, что палиндром симметричен относительно своей центральной позиции. Т.е. если имеем некоторый палиндром длины &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; с центром в позиции &amp;lt;tex&amp;gt;l \leqslant i \leqslant r&amp;lt;/tex&amp;gt;, то в позиции &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, симметричной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; относительно отрезка &amp;lt;tex&amp;gt;[l; r]&amp;lt;/tex&amp;gt; тоже может находиться палиндром длины &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.  Это можно лучше понять, посмотрев на рисунок. Снизу фигурными скобками обозначены равные подстроки. Однако стоит не забыть про один граничный случай: что если &amp;lt;tex&amp;gt;i + d_1[j] - 1&amp;lt;/tex&amp;gt; выходит за границы самого правого палиндрома? Так как информации о том, что происходит за границами это палинлрома у нас нет (а значит мы не можем утверждать, что симметрия сохраняется), то необходимо ограничить значение &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt; следующим образом: &amp;lt;tex&amp;gt;d_1[i] = \min(r - i, d_1[j])&amp;lt;/tex&amp;gt;. После этого запустим наивный алгоритм, который будет увеличивать значение &amp;lt;tex&amp;gt;d_1[i]&amp;lt;/tex&amp;gt;, пока это возможно.&lt;br /&gt;
&lt;br /&gt;
После каждого шага важно не забывать обновлять значения &amp;lt;tex&amp;gt;[l;r]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt; считается аналогичным образом, нужно лишь немного изменить индексы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Манакер.png]]&lt;br /&gt;
&lt;br /&gt;
===Псевдокод===&lt;br /&gt;
Приведем код, который вычисляет значения массива &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} исходная строка&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''int[]''' calculate1('''string''' s):&lt;br /&gt;
   '''int''' l = 0&lt;br /&gt;
   '''int''' r = -1&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
     '''int''' k = 0&lt;br /&gt;
     '''if''' i &amp;lt;= r&lt;br /&gt;
        k = min(r - i, d_1[r - i + l])&lt;br /&gt;
     '''while''' i + k + 1 &amp;lt;= n '''and''' i - k - 1 &amp;gt; 0 '''and''' s[i + k + 1] == s[i - k - 1]&lt;br /&gt;
        k++&lt;br /&gt;
      &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;[i] = k&lt;br /&gt;
      '''if''' i + k &amp;gt; r&lt;br /&gt;
        l = i - k&lt;br /&gt;
        r = i + k&lt;br /&gt;
   '''return''' &amp;lt;tex&amp;gt;d_1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вычисление значений массива &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} исходная строка&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''int[]''' calculate2('''string''' s):&lt;br /&gt;
   '''int''' l = 0&lt;br /&gt;
   '''int''' r = -1&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
     '''int''' k = 0&lt;br /&gt;
     '''if''' i &amp;lt;= r&lt;br /&gt;
        k = min(r - i + 1, d_2[r - i + l + 1])&lt;br /&gt;
     '''while''' i + k &amp;lt;= n '''and''' i - k - 1 &amp;gt; 0 '''and''' s[i + k] == s[i - k - 1]&lt;br /&gt;
        k++&lt;br /&gt;
      &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;[i] = k&lt;br /&gt;
      '''if''' i + k - 1 &amp;gt; r&lt;br /&gt;
        l = i - k&lt;br /&gt;
        r = i + k - 1&lt;br /&gt;
   '''return''' &amp;lt;tex&amp;gt;d_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Оценка сложности===&lt;br /&gt;
Внешний цикл в приведенном алгоритме выполняется ровно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — длина строки. Попытаемся понять, сколько раз будет выполнен внутренний цикл, ответственный за наивный подсчет значений. Заметим, что каждая итерация вложенного цикла приводит к увеличению &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Действительно, возможны следующие случаи:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;tex&amp;gt;i &amp;gt; r&amp;lt;/tex&amp;gt;, т.е. сразу будет запущен наивный алгоритм и каждая его итерация будет увеличивать значение &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; хотя бы на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# &amp;lt;tex&amp;gt;i \leqslant r&amp;lt;/tex&amp;gt;. Здесь опять два случая:&lt;br /&gt;
## &amp;lt;tex&amp;gt;i + d[j] - 1 \leqslant r&amp;lt;/tex&amp;gt;, но тогда, очевидно, ни одной итерации вложенного цикла выполнено не будет.&lt;br /&gt;
## &amp;lt;tex&amp;gt;i + d[j] - 1 &amp;gt; r&amp;lt;/tex&amp;gt;,  тогда каждая итерация вложенного цикла приведет к увеличению &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; хотя бы на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Т.к. значение &amp;lt;tex&amp;gt;r&amp;lt;/tex&amp;gt; не может увеличиваться более &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; раз, то описанный выше алгоритм работает за время &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Префикс-функция]]&lt;br /&gt;
* [[Z-функция]]&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [[Поиск наибольшей общей подстроки двух строк с использованием хеширования]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://e-maxx.ru/algo/palindromes_count MAXimal :: algo :: Нахождение всех подпалиндромов]&lt;br /&gt;
* [[wikipedia:ru:Поиск_длиннейшей_подстроки-палиндрома| Википедия — Поиск длиннейшей подстроки-палиндрома]]&lt;br /&gt;
* [https://habrahabr.ru/post/276195/ Алгоритмы для поиска палиндромов — Хабр]&lt;br /&gt;
* [http://e-maxx.ru/algo/suffix_array#5 MAXimal :: algo :: Суффиксный массив]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Основные определения. Простые комбинаторные свойства слов]]&lt;/div&gt;</summary>
		<author><name>46.237.5.186</name></author>	</entry>

	</feed>