Алгоритм Мейна-Лоренца — различия между версиями
Mariashka (обсуждение | вклад) (Новая страница: «{{Определение |definition = '''Повтором''' (англ. ''repeatition'') называется непустая строка вида <math>\alpha...») |
м (rollbackEdits.php mass rollback) |
||
| (не показаны 34 промежуточные версии 2 участников) | |||
| Строка 1: | Строка 1: | ||
| − | + | ||
| − | + | '''Алгоритм Мейна-Лоренца''' (англ. ''Main-Lorentz algorithm'') {{---}} алгоритм на строках, позволяющий найти все [[Основные_определения,_связанные_со_строками#repetition | тандемные повторы]] в строке <tex>s[1 \dotsc n]</tex> за <tex>O(n \log n)</tex> | |
| − | |||
| − | |||
| − | '''Алгоритм Мейна-Лоренца''' (англ. ''Main-Lorentz algorithm'') {{---}} алгоритм на строках, позволяющий найти все повторы в строке <tex>s[1 | ||
== Алгоритм == | == Алгоритм == | ||
| − | Так как повторов строке <tex> | + | Так как повторов строке <tex> O(n^2)</tex>, мы не можем хранить их в явном виде. Будем хранить несколько подряд идущих (по индексу конца) повторов одной длины блоками вида <tex>(length, first, last)</tex>, где <tex> length </tex> {{---}} это длина повтора, а <tex> [first, last] </tex> {{---}} промежуток индексов, в каждом из которых заканчивается повтор такой длины. Для каждой длины может быть несколько блоков. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | Данный алгоритм {{---}} это алгоритм типа "разделяй и властвуй": разделим строку пополам, рекурсивно запустимся от каждой половинки {{---}} так мы найдем повторы, которые не пересекают границу раздела. Далее рассмотрим процесс нахождения повторов, которые пересекают границу раздела. Их можно разделить на две группы по положению центра повтора: правые и левые. | |
=== Нахождение правых повтров === | === Нахождение правых повтров === | ||
| − | Рассмотрим строку <tex>t | + | Рассмотрим строку <tex>t</tex>, пусть <tex>shift</tex> {{---}} индекс начала <tex>t</tex> в исходной строке <tex>s</tex>.<br> |
| − | + | # Разобьем ее на две строки <tex> u </tex> и <tex> v </tex>. | |
| − | # Предподсчитаем следующие массивы c помощью [ | + | # Предподсчитаем следующие массивы c помощью [[Z-функция | Z-функции]]: |
| − | # | + | #* <tex> RP[i] = lcp(v[i \dotsc v.len], \, v) </tex>, то есть наибольший общий префикс строк <tex> v[i \dotsc v.len] </tex> и <tex> v </tex>. Нахождение <tex>lcp(a,\,b[i \dotsc b.len])</tex> можно осуществить следующим образом: вычислим для строки <math> a+'\#'+b </math> [[Z-функция | Z-функцию]]. Очевидно, что в таком случае массивом <tex>lcp</tex> будет массив значений Z-функции, начиная с индекса <tex> a.len + 2 </tex>. |
| − | # | + | #* <tex> RS[i] = lcs(v[1 \dotsc i], \, u) </tex>, то есть наибольший общий суффикс строк <tex> v[1 \dotsc i]</tex> и <tex> u </tex>. Нахождение <tex>lcs(a,\,b[1 \dotsc i)</tex> можно осуществить следующим образом: вычислим для строки <math> reverse(a)+'\#'+reverse(b) </math> [[Z-функция | Z-функцию]]. Очевидно, что в таком случае массивом <tex>lcs</tex> будет перевернутый массив значений Z-функции, начиная с индекса <tex> a.len + 2 </tex>. |
| − | # Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины | + | # Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины: для каждого <tex> p \in [1, \, t.len /2]</tex> получим интервал индексов конца повтора в строке <tex> v </tex>: <tex> [x, y] </tex> (по формуле, которую докажем позднее). Добавим полученный интервал к ответу, учитывая смещение в исходной строке <tex> s </tex> : <tex>(2p, x + shift + u.len, y + shift + u.len) </tex> |
| − | |||
Итоговая асимптотика: <tex> O(t) </tex> | Итоговая асимптотика: <tex> O(t) </tex> | ||
| Строка 30: | Строка 20: | ||
{{Утверждение | {{Утверждение | ||
|id=kindscount | |id=kindscount | ||
| − | |statement=<math>2p -RS[p] \ | + | |statement=<math>2p -RS[p] \leqslant i \leqslant p - RP[p + 1]</math>, где <tex>i</tex> {{---}} индекс конца повтора длины <tex>2p</tex> в строке <tex>v</tex>. |
|proof= Рассмотрим правый повтор <tex>ww</tex>.<br> | |proof= Рассмотрим правый повтор <tex>ww</tex>.<br> | ||
| − | Обозначим как <tex>k</tex> ту часть первой полвины повтора, которая принадлежит <tex>u</tex>, а как <tex>l</tex> {{---}} ту часть первого половины, которая принадлежит <tex>v</tex>. Равные им подстроки во первой половине обозначим как <tex>m</tex> и <tex>n</tex>(см. рисунок). | + | Обозначим как <tex>k</tex> ту часть первой полвины повтора, которая принадлежит <tex>u</tex>, а как <tex>l</tex> {{---}} ту часть первого половины, которая принадлежит <tex>v</tex>. Равные им подстроки во первой половине обозначим как <tex>m</tex> и <tex>n</tex> (см. рисунок). |
| + | <i>Разбиение строки <tex>t</tex>, с индексацией <tex>u</tex> и <tex>v</tex> :</i><br> | ||
[[Файл:RightRepetition.png|600px]]<br> | [[Файл:RightRepetition.png|600px]]<br> | ||
Пусть <tex> b </tex> {{---}} длина <tex>k</tex>.<br> | Пусть <tex> b </tex> {{---}} длина <tex>k</tex>.<br> | ||
Заметим, что <tex>w = k + l = m + n</tex> и <tex> k = m, l = n </tex>. <br> | Заметим, что <tex>w = k + l = m + n</tex> и <tex> k = m, l = n </tex>. <br> | ||
Тогда | Тогда | ||
| − | # <tex> k = u[(u.len - b + 1) | + | # <tex> k = u[(u.len - b + 1) \dotsc u.len] = m = v[(i - p + 1) \dotsc p] </tex> |
| − | # <tex> l = v[1 | + | # <tex> l = v[1 \dotsc (i - p)] = n = v[(p + 1) \dotsc i] </tex> |
| − | <tex>(1)</tex> эквивалентно тому, что <tex>u</tex> и <tex>v[1 | + | <tex>(1)</tex> эквивалентно тому, что <tex>u</tex> и <tex>v[1 \dotsc p]</tex> имеют общий суффикс длины не менее <tex>b</tex>: <tex>2p - i = b \leqslant RS[p]</tex>. <br> |
| − | <tex>(2)</tex> эквивалентно тому, что строки <tex> v</tex> и <tex> v[p+1 | + | <tex>(2)</tex> эквивалентно тому, что строки <tex> v</tex> и <tex> v[p+1 \dotsc v.len]</tex> имеют общий префикс длины не менее <tex>p-b = i-p</tex>: <tex>i - p \leqslant RP[p + 1] </tex> |
}} | }} | ||
=== Нахождение левых повтров === | === Нахождение левых повтров === | ||
| − | + | Левые повторы находим аналогично правым, кроме вычисления интервала <tex> [x, y] </tex> для заданного <tex> p</tex> и, как следствие, предподсчета. | |
| − | + | Предподсчитаем с помощью [[Z-функция | Z-функции]] массивы: | |
| − | + | * <tex> LP[i] = lcp(u[i \dotsc u.len], \, v) </tex>, то есть наибольший общий префикс строк <tex> u[i \dotsc u.len] </tex> и <tex> v </tex> | |
| − | + | * <tex> LS[i] = lcs(u[1 \dotsc i], \, u) </tex>, то есть наибольший общий суффикс строк <tex> u[1 \dotsc i] </tex> и <tex> u </tex> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
Докажем следующее утверждение для нахождения интервала <tex> [x, y] </tex>: | Докажем следующее утверждение для нахождения интервала <tex> [x, y] </tex>: | ||
{{Утверждение | {{Утверждение | ||
|id=kindscount | |id=kindscount | ||
| − | |statement=<math> p - LS[u.len - p] \ | + | |statement=<math> p - LS[u.len - p] \leqslant i \leqslant LP[u.len - p + 1] </math>, где <tex>i</tex> {{---}} индекс конца повтора длины <tex>2p</tex> в строке <tex>v</tex>. |
|proof= Рассмотрим правый повтор <tex>ww</tex>.<br> | |proof= Рассмотрим правый повтор <tex>ww</tex>.<br> | ||
| − | Обозначим как <tex>m</tex> ту часть первой второй повтора, которая принадлежит <tex>u</tex>, а как <tex>n</tex> {{---}} ту часть второго половины, которая принадлежит <tex>v</tex>. Равные им подстроки во второй половине обозначим как <tex>k</tex> и <tex>l</tex>(см. рисунок). | + | Обозначим как <tex>m</tex> ту часть первой второй повтора, которая принадлежит <tex>u</tex>, а как <tex>n</tex> {{---}} ту часть второго половины, которая принадлежит <tex>v</tex>. Равные им подстроки во второй половине обозначим как <tex>k</tex> и <tex>l</tex> (см. рисунок). |
| + | <i>Разбиение строки <tex>t</tex>, с индексацией <tex>u</tex> и <tex>v</tex>:</i><br> | ||
[[Файл:LeftRepetition.png|600px]]<br> | [[Файл:LeftRepetition.png|600px]]<br> | ||
| + | |||
Пусть <tex> b </tex> {{---}} длина <tex>k+l+m</tex>. | Пусть <tex> b </tex> {{---}} длина <tex>k+l+m</tex>. | ||
Заметим, что <tex>w = k + l = m + n</tex> и <tex> k = m, l = n </tex>. <br> | Заметим, что <tex>w = k + l = m + n</tex> и <tex> k = m, l = n </tex>. <br> | ||
Тогда | Тогда | ||
| − | # <tex> k = u[(u.len - b + 1) | + | # <tex> k = u[(u.len - b + 1) \dotsc (u.len - p)] = m = u[(u.len - b + p + 1) \dotsc u.len] </tex> |
| − | # <tex> l = u[(u.len - p + 1) | + | # <tex> l = u[(u.len - p + 1) \dotsc (u.len - b + p)] = n = v[1 \dotsc . i] </tex> |
| − | <tex>(1)</tex> эквивалентно тому, что <tex>u</tex> и <tex>u[(u.len - b + 1) | + | <tex>(1)</tex> эквивалентно тому, что <tex>u</tex> и <tex>u[(u.len - b + 1) \dotsc u.len]</tex> имеют общий префикс длины не менее <tex>b - p = p - i</tex>: <tex> p - i \leqslant LS[u.len - p]</tex>. <br> |
| − | <tex>(2)</tex> эквивалентно тому, что строки <tex> v</tex> и <tex> u[(u.len - p) | + | <tex>(2)</tex> эквивалентно тому, что строки <tex> v</tex> и <tex> u[(u.len - p) \dotsc u.len]</tex> имеют общий суффикс длины не менее <tex>i</tex>: <tex>i \leqslant LP[u.len - p + 1] </tex> |
}} | }} | ||
== Асимптотика == | == Асимптотика == | ||
| − | + | Асимптотика алгоритма "разделяй и властвуй", каждый рекурсивный запуск которого линеен относительно длины строки, <tex> O(n \log n) </tex> из рекурентного соотношения <tex>T(n)=2T(n/2)+O(n)</tex> (аналогичное доказательство для [[Сортировка слиянием | сортировки слиянием]]). | |
| + | |||
| + | Количество блоков в ответе также будет <tex> O(n \log n) </tex>: на каждом рекурсивном запуске при рассмотрении повторов, которые пересекают границу раздела, добавляется <tex> O(1) </tex> блоков для каждой рассмотренной длины повтора (их количество линейно относительно длины строки), из чего получаем аналогичное рекурентное соотношение <tex>M(n)=2M(n/2)+O(n)</tex>. | ||
| + | |||
| + | == См. также == | ||
| − | + | * [[Алгоритм Ландау-Шмидта]] | |
| + | * [[Алгоритм Крочемора]] | ||
| − | == Источники == | + | == Источники информации == |
* ''Main, M., Lorentz, R.J.'' — '''An O(n log n) Algorithm for Finding All Repetitions in a String'''. 1982 | * ''Main, M., Lorentz, R.J.'' — '''An O(n log n) Algorithm for Finding All Repetitions in a String'''. 1982 | ||
* ''Билл Смит'' — '''Методы и алгоритмы вычислений на строках'''. Пер. с англ.{{---}} М.:Издательский дом "Вильямс", 2006. ISBN 5-8459-1081-1 | * ''Билл Смит'' — '''Методы и алгоритмы вычислений на строках'''. Пер. с англ.{{---}} М.:Издательский дом "Вильямс", 2006. ISBN 5-8459-1081-1 | ||
| + | * [http://e-maxx.ru/algo/string_tandems MAXimal :: algo :: Поиск всех тандемных повторов в строке. Алгоритм Мейна-Лоренца] | ||
| + | |||
| + | |||
| + | [[Категория: Алгоритмы и структуры данных]] | ||
| + | [[Категория: Основные определения. Простые комбинаторные свойства слов]] | ||
Текущая версия на 19:13, 4 сентября 2022
Алгоритм Мейна-Лоренца (англ. Main-Lorentz algorithm) — алгоритм на строках, позволяющий найти все тандемные повторы в строке за
Содержание
Алгоритм
Так как повторов строке , мы не можем хранить их в явном виде. Будем хранить несколько подряд идущих (по индексу конца) повторов одной длины блоками вида , где — это длина повтора, а — промежуток индексов, в каждом из которых заканчивается повтор такой длины. Для каждой длины может быть несколько блоков.
Данный алгоритм — это алгоритм типа "разделяй и властвуй": разделим строку пополам, рекурсивно запустимся от каждой половинки — так мы найдем повторы, которые не пересекают границу раздела. Далее рассмотрим процесс нахождения повторов, которые пересекают границу раздела. Их можно разделить на две группы по положению центра повтора: правые и левые.
Нахождение правых повтров
Рассмотрим строку , пусть — индекс начала в исходной строке .
- Разобьем ее на две строки и .
- Предподсчитаем следующие массивы c помощью Z-функции:
- , то есть наибольший общий префикс строк и . Нахождение можно осуществить следующим образом: вычислим для строки Z-функцию. Очевидно, что в таком случае массивом будет массив значений Z-функции, начиная с индекса .
- , то есть наибольший общий суффикс строк и . Нахождение можно осуществить следующим образом: вычислим для строки Z-функцию. Очевидно, что в таком случае массивом будет перевернутый массив значений Z-функции, начиная с индекса .
- Переберем длину повтора и будем искать все повторы такой длины: для каждого получим интервал индексов конца повтора в строке : (по формуле, которую докажем позднее). Добавим полученный интервал к ответу, учитывая смещение в исходной строке :
Итоговая асимптотика:
Докажем следующее утверждение для нахождения интервала :
Нахождение левых повтров
Левые повторы находим аналогично правым, кроме вычисления интервала для заданного и, как следствие, предподсчета. Предподсчитаем с помощью Z-функции массивы:
- , то есть наибольший общий префикс строк и
- , то есть наибольший общий суффикс строк и
Докажем следующее утверждение для нахождения интервала :
Асимптотика
Асимптотика алгоритма "разделяй и властвуй", каждый рекурсивный запуск которого линеен относительно длины строки, из рекурентного соотношения (аналогичное доказательство для сортировки слиянием).
Количество блоков в ответе также будет : на каждом рекурсивном запуске при рассмотрении повторов, которые пересекают границу раздела, добавляется блоков для каждой рассмотренной длины повтора (их количество линейно относительно длины строки), из чего получаем аналогичное рекурентное соотношение .
См. также
Источники информации
- Main, M., Lorentz, R.J. — An O(n log n) Algorithm for Finding All Repetitions in a String. 1982
- Билл Смит — Методы и алгоритмы вычислений на строках. Пер. с англ.— М.:Издательский дом "Вильямс", 2006. ISBN 5-8459-1081-1
- MAXimal :: algo :: Поиск всех тандемных повторов в строке. Алгоритм Мейна-Лоренца