Алгоритм Мейна-Лоренца — различия между версиями
Mariashka (обсуждение | вклад) |
Mariashka (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | '''Алгоритм Мейна-Лоренца''' (англ. ''Main-Lorentz algorithm'') {{---}} алгоритм на строках, позволяющий найти все [[#repeation | повторы]] в строке <tex>s[1 | + | '''Алгоритм Мейна-Лоренца''' (англ. ''Main-Lorentz algorithm'') {{---}} алгоритм на строках, позволяющий найти все [[#repeation | повторы]] в строке <tex>s[1 \dotsc n]</tex> за <tex>O(n \log n)</tex> |
== Алгоритм == | == Алгоритм == | ||
Строка 11: | Строка 11: | ||
# Предподсчитаем следующие массивы c помощью [[Z-функция | Z-функции]]: | # Предподсчитаем следующие массивы c помощью [[Z-функция | Z-функции]]: | ||
− | ## <tex> RP[i] = lcp(v[i | + | ## <tex> RP[i] = lcp(v[i \dotsc v.len], v) </tex>, то есть наибольший общий префикс строк <tex> v[i \dotsc v.len] </tex> и <tex> v </tex> |
− | ## <tex> RS[i] = lcs(v[1 | + | ## <tex> RS[i] = lcs(v[1 \dotsc i], u) </tex>, то есть наибольший общий суффикс строк <tex> v[1 \dotsc i]</tex> и <tex> u </tex> |
# Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины. Для этого для каждого <tex> p </tex> получим интервал индексов конца повтора в строке <tex> v </tex>: <tex> [x, y] </tex>(позднее покажем, как это сделать). | # Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины. Для этого для каждого <tex> p </tex> получим интервал индексов конца повтора в строке <tex> v </tex>: <tex> [x, y] </tex>(позднее покажем, как это сделать). | ||
# Добавим к ответу, учитывая смещение в исходной строке <tex> s </tex> : <tex>(2p, x + shift + u.len, y + shift + u.len) </tex> | # Добавим к ответу, учитывая смещение в исходной строке <tex> s </tex> : <tex>(2p, x + shift + u.len, y + shift + u.len) </tex> | ||
Строка 29: | Строка 29: | ||
Заметим, что <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> |
}} | }} | ||
Строка 39: | Строка 39: | ||
# Предподсчитаем следующие массивы с помощью [[Z-функция | Z-функции]]: | # Предподсчитаем следующие массивы с помощью [[Z-функция | Z-функции]]: | ||
− | ## <tex> LP[i] = lcp(u[i | + | ## <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 | + | ## <tex> LS[i] = lcs(u[1 \dotsc i], u) </tex>, где <tex> lcs </tex> {{---}} наибольший общий суффикс строк <tex> u[1 \dotsc i] </tex> и <tex> u </tex> |
# Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины. Для этого для каждого <tex> p </tex> получим интервал индексов конца повтора в строке <tex> v </tex>: <tex> [x, y] </tex>(позднее покажем, как это сделать). | # Переберем длину повтора <tex> 2p </tex> и будем искать все повторы такой длины. Для этого для каждого <tex> p </tex> получим интервал индексов конца повтора в строке <tex> v </tex>: <tex> [x, y] </tex>(позднее покажем, как это сделать). | ||
# Добавим к ответу, учитывая смещение в исходной строке <tex> s </tex> : <tex>(2p, x + shift + u.len, y + shift + u.len) </tex> | # Добавим к ответу, учитывая смещение в исходной строке <tex> s </tex> : <tex>(2p, x + shift + u.len, y + shift + u.len) </tex> | ||
Строка 57: | Строка 57: | ||
Заметим, что <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> O(n \log n) </tex>, так как при каждом рекрсивном запуске добавляется <tex> O(1) </tex> блоков для каждой рассмотренной длины повтора, а их количество линейно относительно длины строки. |
Версия 16:42, 30 апреля 2015
Алгоритм Мейна-Лоренца (англ. Main-Lorentz algorithm) — алгоритм на строках, позволяющий найти все повторы в строке за
Содержание
Алгоритм
Так как повторов строке
, мы не можем хранить их в явном виде. Будем хранить повторы блоками вида , где — это длина повтора, а — промежуток индексов, в каждом из которых заканчивается повтор такой длины. Для каждой длины может быть несколько блоков.Данный алгоритм — это алгоритм типа "разделяй и властвуй": разделим строку пополам, рекурсивно запустимся от каждой половинки — так мы найдем повторы, которые не пересекают границу раздела. Далее рассмотрим процесс нахождения повторов, которые пересекают границу раздела. Их можно разделить на две группы по положению центра повтора: правые и левые.
Нахождение правых повтров
Рассмотрим строку
, пусть — индекс начала в исходной строке- Предподсчитаем следующие массивы c помощью 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