Алгоритм Мейна-Лоренца

Материал из Викиконспекты
Перейти к: навигация, поиск
НЕТ ВОЙНЕ

24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.

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

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

Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.

Антивоенный комитет России

Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
meduza.io, Популярная политика, Новая газета, zona.media, Майкл Наки.


Алгоритм Мейна-Лоренца (англ. Main-Lorentz algorithm) — алгоритм на строках, позволяющий найти все тандемные повторы в строке [math]s[1 \dotsc n][/math] за [math]O(n \log n)[/math]

Алгоритм

Так как повторов строке [math] O(n^2)[/math], мы не можем хранить их в явном виде. Будем хранить несколько подряд идущих (по индексу конца) повторов одной длины блоками вида [math](length, first, last)[/math], где [math] length [/math] — это длина повтора, а [math] [first, last] [/math] — промежуток индексов, в каждом из которых заканчивается повтор такой длины. Для каждой длины может быть несколько блоков.

Данный алгоритм — это алгоритм типа "разделяй и властвуй": разделим строку пополам, рекурсивно запустимся от каждой половинки — так мы найдем повторы, которые не пересекают границу раздела. Далее рассмотрим процесс нахождения повторов, которые пересекают границу раздела. Их можно разделить на две группы по положению центра повтора: правые и левые.

Нахождение правых повтров

Рассмотрим строку [math]t[/math], пусть [math]shift[/math] — индекс начала [math]t[/math] в исходной строке [math]s[/math].

  1. Разобьем ее на две строки [math] u [/math] и [math] v [/math].
  2. Предподсчитаем следующие массивы c помощью Z-функции:
    • [math] RP[i] = lcp(v[i \dotsc v.len], \, v) [/math], то есть наибольший общий префикс строк [math] v[i \dotsc v.len] [/math] и [math] v [/math]. Нахождение [math]lcp(a,\,b[i \dotsc b.len])[/math] можно осуществить следующим образом: вычислим для строки [math] a+'\#'+b [/math] Z-функцию. Очевидно, что в таком случае массивом [math]lcp[/math] будет массив значений Z-функции, начиная с индекса [math] a.len + 2 [/math].
    • [math] RS[i] = lcs(v[1 \dotsc i], \, u) [/math], то есть наибольший общий суффикс строк [math] v[1 \dotsc i][/math] и [math] u [/math]. Нахождение [math]lcs(a,\,b[1 \dotsc i)[/math] можно осуществить следующим образом: вычислим для строки [math] reverse(a)+'\#'+reverse(b) [/math] Z-функцию. Очевидно, что в таком случае массивом [math]lcs[/math] будет перевернутый массив значений Z-функции, начиная с индекса [math] a.len + 2 [/math].
  3. Переберем длину повтора [math] 2p [/math] и будем искать все повторы такой длины: для каждого [math] p \in [1, \, t.len /2][/math] получим интервал индексов конца повтора в строке [math] v [/math]: [math] [x, y] [/math] (по формуле, которую докажем позднее). Добавим полученный интервал к ответу, учитывая смещение в исходной строке [math] s [/math] : [math](2p, x + shift + u.len, y + shift + u.len) [/math]

Итоговая асимптотика: [math] O(t) [/math]

Докажем следующее утверждение для нахождения интервала [math] [x, y] [/math]:

Утверждение:
[math]2p -RS[p] \leqslant i \leqslant p - RP[p + 1][/math], где [math]i[/math] — индекс конца повтора длины [math]2p[/math] в строке [math]v[/math].
[math]\triangleright[/math]

Рассмотрим правый повтор [math]ww[/math].
Обозначим как [math]k[/math] ту часть первой полвины повтора, которая принадлежит [math]u[/math], а как [math]l[/math] — ту часть первого половины, которая принадлежит [math]v[/math]. Равные им подстроки во первой половине обозначим как [math]m[/math] и [math]n[/math] (см. рисунок).

Разбиение строки [math]t[/math], с индексацией [math]u[/math] и [math]v[/math] :
RightRepetition.png
Пусть [math] b [/math] — длина [math]k[/math].
Заметим, что [math]w = k + l = m + n[/math] и [math] k = m, l = n [/math].
Тогда

  1. [math] k = u[(u.len - b + 1) \dotsc u.len] = m = v[(i - p + 1) \dotsc p] [/math]
  2. [math] l = v[1 \dotsc (i - p)] = n = v[(p + 1) \dotsc i] [/math]

[math](1)[/math] эквивалентно тому, что [math]u[/math] и [math]v[1 \dotsc p][/math] имеют общий суффикс длины не менее [math]b[/math]: [math]2p - i = b \leqslant RS[p][/math].

[math](2)[/math] эквивалентно тому, что строки [math] v[/math] и [math] v[p+1 \dotsc v.len][/math] имеют общий префикс длины не менее [math]p-b = i-p[/math]: [math]i - p \leqslant RP[p + 1] [/math]
[math]\triangleleft[/math]

Нахождение левых повтров

Левые повторы находим аналогично правым, кроме вычисления интервала [math] [x, y] [/math] для заданного [math] p[/math] и, как следствие, предподсчета. Предподсчитаем с помощью Z-функции массивы:

  • [math] LP[i] = lcp(u[i \dotsc u.len], \, v) [/math], то есть наибольший общий префикс строк [math] u[i \dotsc u.len] [/math] и [math] v [/math]
  • [math] LS[i] = lcs(u[1 \dotsc i], \, u) [/math], то есть наибольший общий суффикс строк [math] u[1 \dotsc i] [/math] и [math] u [/math]

Докажем следующее утверждение для нахождения интервала [math] [x, y] [/math]:

Утверждение:
[math] p - LS[u.len - p] \leqslant i \leqslant LP[u.len - p + 1] [/math], где [math]i[/math] — индекс конца повтора длины [math]2p[/math] в строке [math]v[/math].
[math]\triangleright[/math]

Рассмотрим правый повтор [math]ww[/math].
Обозначим как [math]m[/math] ту часть первой второй повтора, которая принадлежит [math]u[/math], а как [math]n[/math] — ту часть второго половины, которая принадлежит [math]v[/math]. Равные им подстроки во второй половине обозначим как [math]k[/math] и [math]l[/math] (см. рисунок).

Разбиение строки [math]t[/math], с индексацией [math]u[/math] и [math]v[/math]:
LeftRepetition.png

Пусть [math] b [/math] — длина [math]k+l+m[/math]. Заметим, что [math]w = k + l = m + n[/math] и [math] k = m, l = n [/math].
Тогда

  1. [math] k = u[(u.len - b + 1) \dotsc (u.len - p)] = m = u[(u.len - b + p + 1) \dotsc u.len] [/math]
  2. [math] l = u[(u.len - p + 1) \dotsc (u.len - b + p)] = n = v[1 \dotsc . i] [/math]

[math](1)[/math] эквивалентно тому, что [math]u[/math] и [math]u[(u.len - b + 1) \dotsc u.len][/math] имеют общий префикс длины не менее [math]b - p = p - i[/math]: [math] p - i \leqslant LS[u.len - p][/math].

[math](2)[/math] эквивалентно тому, что строки [math] v[/math] и [math] u[(u.len - p) \dotsc u.len][/math] имеют общий суффикс длины не менее [math]i[/math]: [math]i \leqslant LP[u.len - p + 1] [/math]
[math]\triangleleft[/math]

Асимптотика

Асимптотика алгоритма "разделяй и властвуй", каждый рекурсивный запуск которого линеен относительно длины строки, [math] O(n \log n) [/math] из рекурентного соотношения [math]T(n)=2T(n/2)+O(n)[/math] (аналогичное доказательство для сортировки слиянием).

Количество блоков в ответе также будет [math] O(n \log n) [/math]: на каждом рекурсивном запуске при рассмотрении повторов, которые пересекают границу раздела, добавляется [math] O(1) [/math] блоков для каждой рассмотренной длины повтора (их количество линейно относительно длины строки), из чего получаем аналогичное рекурентное соотношение [math]M(n)=2M(n/2)+O(n)[/math].

См. также

Источники информации