Количество подпалиндромов в строке — различия между версиями
| Строка 1: | Строка 1: | ||
{{Шаблон:Задача | {{Шаблон:Задача | ||
|definition = | |definition = | ||
| − | Пусть дана строка <tex>s</tex>, требуется посчитать количество [[Основные_определения,_связанные_со_строками#palindrome | палиндромов]] в ней | + | Пусть дана строка <tex>s</tex>, требуется посчитать количество [[Основные_определения,_связанные_со_строками#palindrome | палиндромов]] в ней. |
}} | }} | ||
== Алгоритм == | == Алгоритм == | ||
=== Идея === | === Идея === | ||
| − | Рассмотрим сначала задачу поиска палиндромов нечетной длины. Для каждой позиции в строке <tex>s</tex> найдем длину наибольшего палиндрома с центром в этой позиции. Очевидно, что если строка <tex>t</tex> является палиндромом, то строка полученная вычеркиванием первого и последнего символа из <tex>t</tex> также является палиндромом | + | Рассмотрим сначала задачу поиска палиндромов нечетной длины. Для каждой позиции в строке <tex>s</tex> найдем длину наибольшего палиндрома с центром в этой позиции. Очевидно, что если строка <tex>t</tex> является палиндромом, то строка полученная вычеркиванием первого и последнего символа из <tex>t</tex> также является палиндромом, поэтому длину палиндрома можно искать [[Целочисленный_двоичный_поиск | бинарным поиском]]. Проверить совпадение левой и правой половины можно выполнить за <tex>O(1)</tex>, используя метод хеширования. |
Для палиндромов четной длины алгоритм такой же, только следует проверять вторую строку со сдвигом на единицу, при этом мы не посчитаем никакой палиндром дважды из-за четности-нечетности палиндромов. | Для палиндромов четной длины алгоритм такой же, только следует проверять вторую строку со сдвигом на единицу, при этом мы не посчитаем никакой палиндром дважды из-за четности-нечетности палиндромов. | ||
| Строка 13: | Строка 13: | ||
'''int''' binarySearch(s : '''string''', center, shift : '''int'''): | '''int''' binarySearch(s : '''string''', center, shift : '''int'''): | ||
''<font color=green>//shift = 0 при поиске палиндрома нечетной длины, иначе shift = 1</font>'' | ''<font color=green>//shift = 0 при поиске палиндрома нечетной длины, иначе shift = 1</font>'' | ||
| − | '''int''' l = -1, r = s.length, m = 0 | + | '''int''' l = -1, r = min(center, s.length - center + shift), m = 0 |
'''while''' r - l != 1 | '''while''' r - l != 1 | ||
m = l + (r - l) / 2 | m = l + (r - l) / 2 | ||
| Строка 27: | Строка 27: | ||
ans += binarySearch(s, i, 0) + binarySearch(s, i, 1) | ans += binarySearch(s, i, 0) + binarySearch(s, i, 1) | ||
'''return''' ans | '''return''' ans | ||
| + | |||
| + | === Время работы === | ||
| + | Изначальный подсчет хешей производится за <tex>O(|s|)</tex>. Каждая итерация будет выполняться за <tex>O(\log(|s|))</tex>, всего итераций {{---}} <tex>|s|</tex>. Итоговое время работы алгоритма <tex>O(|s|+|s|\cdot \log(|s|)) = O(|s|\cdot \log(|s|))</tex>. | ||
=== Избавление от коллизий === | === Избавление от коллизий === | ||
| − | У хешей есть один недостаток {{---}} коллизии, | + | У хешей есть один недостаток {{---}} коллизии, возможно подобрать входные данные так, что хеши разных строк будут совпадать. Абсолютно точно проверить две подстроки на совпадение можно с помощью [[Суффиксный массив | суффиксного массива]], но с дополнительной памятью <tex>O(|s|\cdot \log(|s|)</tex>. Для этого построим суффиксный массив для строки <tex>s + \# + reverse(s)</tex>, при этом сохраним промежуточные результаты классов эквивалентности <tex>c</tex>. Пусть нам требуется проверить на совпадение подстроки <tex>s[i..i + l]</tex> и <tex>s[j..j + l]</tex>. Разобьем каждую нашу строку на две пересекающиеся подстроки длиной <tex>2^k</tex>, где <tex>k = \lfloor \log{l} \rfloor</tex>. Тогда наши строки совпадают, если <tex>c[k][i] = c[k][j]</tex> и <tex>c[k][i + l - 2^k] = c[k][j + l - 2^k]</tex>. |
| − | Итоговая асимптотика алгоритма: предподсчет за построение суффиксного массива и <tex>O(\log(|s|)</tex> на запрос, если предподсчитать все <tex>k</tex>, то <tex>O(1)</tex>. | + | Итоговая асимптотика алгоритма: предподсчет за построение суффиксного массива и <tex>O(\log(|s|))</tex> на запрос, если предподсчитать все <tex>k</tex>, то <tex>O(1)</tex>. |
==См. также== | ==См. также== | ||
Версия 00:00, 31 марта 2016
| Задача: |
| Пусть дана строка , требуется посчитать количество палиндромов в ней. |
Содержание
Алгоритм
Идея
Рассмотрим сначала задачу поиска палиндромов нечетной длины. Для каждой позиции в строке найдем длину наибольшего палиндрома с центром в этой позиции. Очевидно, что если строка является палиндромом, то строка полученная вычеркиванием первого и последнего символа из также является палиндромом, поэтому длину палиндрома можно искать бинарным поиском. Проверить совпадение левой и правой половины можно выполнить за , используя метод хеширования.
Для палиндромов четной длины алгоритм такой же, только следует проверять вторую строку со сдвигом на единицу, при этом мы не посчитаем никакой палиндром дважды из-за четности-нечетности палиндромов.
Псевдокод
int binarySearch(s : string, center, shift : int):
//shift = 0 при поиске палиндрома нечетной длины, иначе shift = 1
int l = -1, r = min(center, s.length - center + shift), m = 0
while r - l != 1
m = l + (r - l) / 2
if hash(s[center - m..center]) == hash(reverse(s[center + shift..center + shift + m]))
l = m
else
r = m
return r
int palindromesCount(s : string):
int ans = 0
for i = 0 to s.length
ans += binarySearch(s, i, 0) + binarySearch(s, i, 1)
return ans
Время работы
Изначальный подсчет хешей производится за . Каждая итерация будет выполняться за , всего итераций — . Итоговое время работы алгоритма .
Избавление от коллизий
У хешей есть один недостаток — коллизии, возможно подобрать входные данные так, что хеши разных строк будут совпадать. Абсолютно точно проверить две подстроки на совпадение можно с помощью суффиксного массива, но с дополнительной памятью . Для этого построим суффиксный массив для строки , при этом сохраним промежуточные результаты классов эквивалентности . Пусть нам требуется проверить на совпадение подстроки и . Разобьем каждую нашу строку на две пересекающиеся подстроки длиной , где . Тогда наши строки совпадают, если и .
Итоговая асимптотика алгоритма: предподсчет за построение суффиксного массива и на запрос, если предподсчитать все , то .
См. также