Декомпозиция Линдона — различия между версиями
Xottab (обсуждение | вклад) м (→Источники информации) |
м (rollbackEdits.php mass rollback) |
||
(не показано 18 промежуточных версий 7 участников) | |||
Строка 5: | Строка 5: | ||
{{Определение | {{Определение | ||
|id=def1. | |id=def1. | ||
− | |definition='''Простая строка''' {{---}} строка, которая лексикографически меньше любого своего суффикса. | + | |definition='''Простая строка''' {{---}} строка, которая лексикографически меньше любого своего собственного суффикса. |
}} | }} | ||
Строка 16: | Строка 16: | ||
{{Определение | {{Определение | ||
|id=def2. | |id=def2. | ||
− | |definition='''Декомпозиция Линдона''' (англ. ''Lyndon decomposition'') строки <tex>s</tex> {{---}} её разложение <tex>s = s_1s_2 | + | |definition='''Декомпозиция Линдона''' (англ. ''Lyndon decomposition'') строки <tex>s</tex> {{---}} её разложение <tex>s = s_1s_2 \ldots s_k</tex>, где строки <tex>s_i</tex> просты, и при этом <tex>s_1 \geqslant s_2 \geqslant \ldots\geqslant s_k</tex>. |
}} | }} | ||
Строка 27: | Строка 27: | ||
2. <tex>s + t</tex> {{---}} простая | 2. <tex>s + t</tex> {{---}} простая | ||
|proof= | |proof= | ||
− | 1. Так как <tex>s < t</tex>, то <tex>\exists i : s[i] < t[i]</tex> и <tex>s[j] = t[j]</tex>, <tex>j < i | + | 1. Так как <tex>s < t</tex>, то либо <tex>s</tex> является префиксом <tex>t</tex>, тогда: |
+ | |||
+ | <tex>s + t = s + s + x</tex> | ||
+ | |||
+ | <tex>s + s + x < s + x</tex> | ||
+ | |||
+ | <tex>s + x < x</tex> | ||
+ | |||
+ | Следовательно <tex>t <</tex> любого суффикса <tex>t</tex> (так как по условию <tex>t</tex> явлеяется простой строкой), либо <tex>\exists i : s[i] < t[i]</tex> и <tex>s[j] = t[j]</tex>, <tex>j < i</tex>. | ||
+ | |||
+ | Из обоих ситуаций следует, что <tex> s + t < t</tex> | ||
2. Пусть <tex>u</tex> {{---}} суффикс строки <tex>s + t</tex>. Тогда рассмотрим 3 возможных случая: | 2. Пусть <tex>u</tex> {{---}} суффикс строки <tex>s + t</tex>. Тогда рассмотрим 3 возможных случая: | ||
Строка 33: | Строка 43: | ||
* <tex>|u| = |t| \Rightarrow u = t \Rightarrow u > s + t</tex> по пункту 1 | * <tex>|u| = |t| \Rightarrow u = t \Rightarrow u > s + t</tex> по пункту 1 | ||
* <tex>|u| < |t| \Rightarrow u</tex> {{---}} суффикс <tex>t</tex>. Так как <tex>t</tex> {{---}} простая, и <tex>t < u </tex> по определению <tex> \Rightarrow s + t < t < u</tex> | * <tex>|u| < |t| \Rightarrow u</tex> {{---}} суффикс <tex>t</tex>. Так как <tex>t</tex> {{---}} простая, и <tex>t < u </tex> по определению <tex> \Rightarrow s + t < t < u</tex> | ||
− | * <tex>|u| > |t| \Rightarrow s = s' + s''</tex>, <tex>u = s'' + t</tex>. Так как <tex>s</tex> {{---}} простая, то её суффикс <tex> s'' </tex> | + | * <tex>|u| > |t| \Rightarrow s = s' + s''</tex>, <tex>u = s'' + t</tex>. Так как <tex>s</tex> {{---}} простая, то её суффикс <tex> s'' </tex> больше самой строки <tex> s </tex> в каком-то символе, значит, <tex> s + t < s'' + t</tex> |
}} | }} | ||
Строка 47: | Строка 57: | ||
Предположим, что это не так. Значит, <tex>\exists i : s_i < s_{i+1}</tex>. Так как слова <tex> s_i </tex> и <tex> s_{i+1} </tex> простые, то из доказанной [[Декомпозиция Линдона#lemma | леммы]] следует, что эти слова можно сконкатенировать и получить разбиение строки <tex> s </tex> на меньшее число слов. Получили противоречие. | Предположим, что это не так. Значит, <tex>\exists i : s_i < s_{i+1}</tex>. Так как слова <tex> s_i </tex> и <tex> s_{i+1} </tex> простые, то из доказанной [[Декомпозиция Линдона#lemma | леммы]] следует, что эти слова можно сконкатенировать и получить разбиение строки <tex> s </tex> на меньшее число слов. Получили противоречие. | ||
− | Таким образом доказали даже более сильное утверждение: <tex>s = s_1 s_2 | + | Таким образом доказали даже более сильное утверждение: <tex>s = s_1 s_2 \ldots s_k</tex>, <tex> k </tex> {{---}} минимально <tex>\Leftrightarrow</tex> нет <tex>s_i < s_{i+1}</tex> |
'''2. Единственность.''' | '''2. Единственность.''' | ||
− | Пусть существует несколько разбиений <tex>s = s_1s_2 | + | Пусть существует несколько разбиений <tex>s = s_1s_2 \ldots s_k = s_1's_2' \ldots s_k'</tex>, |
удовлетворяющих условию теоремы. | удовлетворяющих условию теоремы. | ||
Сравним длины первых двух слов <tex>s_1</tex> и <tex>s_1'</tex>, если <tex>|s_1| = |s_1'|</tex>, сравним вторые и так далее. | Сравним длины первых двух слов <tex>s_1</tex> и <tex>s_1'</tex>, если <tex>|s_1| = |s_1'|</tex>, сравним вторые и так далее. | ||
Строка 59: | Строка 69: | ||
Покажем, что такого не может быть: | Покажем, что такого не может быть: | ||
− | 1) Пусть <tex>|s_i| > |s_i'|</tex>, тогда <tex>s_i = s_i's_{i+1}' | + | 1) Пусть <tex>|s_i| > |s_i'|</tex>, тогда <tex>s_i = s_i's_{i+1}' \ldots t</tex>, где <tex>t</tex> {{---}} префикс <tex>s_{j+1}'</tex>, <tex>i < j</tex>. Тогда получаем: |
* <tex>s_i < t</tex> (<tex>s_i</tex> {{---}} простая cтрока и по определению меньше своего суффикса) | * <tex>s_i < t</tex> (<tex>s_i</tex> {{---}} простая cтрока и по определению меньше своего суффикса) | ||
* <tex>t < s_{j+1}'</tex> (<tex>t</tex> {{---}} префикс <tex>s_{j+1}'</tex>) | * <tex>t < s_{j+1}'</tex> (<tex>t</tex> {{---}} префикс <tex>s_{j+1}'</tex>) | ||
Строка 115: | Строка 125: | ||
===Корректность=== | ===Корректность=== | ||
− | Покажем, что алгоритм получает нужное разложение. То есть все <tex>s_i</tex> {{---}} простые, и <tex>s_1 \geqslant s_2 \geqslant | + | Покажем, что алгоритм получает нужное разложение. То есть все <tex>s_i</tex> {{---}} простые, и <tex>s_1 \geqslant s_2 \geqslant \ldots \geqslant s_k</tex> лексикографически. |
При обработке текущего символа в первом случае просто сдвигаем указатели, не записывая ответ. Мы сравниваем символы в <tex> w </tex> и <tex> w' </tex> на одинаковых позициях, а <tex> w' </tex> {{---}} префикс <tex> w </tex>, поэтому инвариант сохраняется. | При обработке текущего символа в первом случае просто сдвигаем указатели, не записывая ответ. Мы сравниваем символы в <tex> w </tex> и <tex> w' </tex> на одинаковых позициях, а <tex> w' </tex> {{---}} префикс <tex> w </tex>, поэтому инвариант сохраняется. | ||
Строка 146: | Строка 156: | ||
Покажем, что <tex>\forall\tau: 1\leqslant\tau\log{n}</tex> существует структура данных, размер которой линейно зависит от длины данного текста, со временем запроса <tex>O(\tau)</tex> и временем препроцессинга <tex>O(n\log{n/\tau})</tex> для запросов на нахождение минимального суффикса. | Покажем, что <tex>\forall\tau: 1\leqslant\tau\log{n}</tex> существует структура данных, размер которой линейно зависит от длины данного текста, со временем запроса <tex>O(\tau)</tex> и временем препроцессинга <tex>O(n\log{n/\tau})</tex> для запросов на нахождение минимального суффикса. | ||
− | Для данных индексов <tex>i<j</tex> будем обозначать как <tex>Suf[i,j]</tex> массив <tex>\{T[i\ldots], \ldots , T[j\ldots]\}</tex> - подмассив с индекса <tex>i</tex> по <tex>j</tex> массива всех суффиксов строки. Множество всех непустых суффиксов строки <tex>Suf[1,|T|]</tex> будем обозначать для краткости как <tex>Suf</tex>. Также, будем обозначать <tex>SA(T)</tex> и <tex>ISA(T)</tex> [[суффиксный массив ]] и инвертированный суффиксный массив строки <tex>T</tex> соответственно. <tex>SA</tex> и <tex>ISA</tex> могут быть улучшены за <tex>O(n)</tex>, чтобы отвечать на запросы вида | + | Для данных индексов <tex>i<j</tex> будем обозначать как <tex>Suf[i,j]</tex> массив <tex>\{T[i\ldots], \ldots , T[j\ldots]\}</tex> - подмассив с индекса <tex>i</tex> по <tex>j</tex> массива всех суффиксов строки. Множество всех непустых суффиксов строки <tex>Suf[1,|T|]</tex> будем обозначать для краткости как <tex>Suf</tex>. Также, будем обозначать <tex>SA(T)</tex> и <tex>ISA(T)</tex> [[суффиксный массив]] и инвертированный суффиксный массив строки <tex>T</tex> соответственно. <tex>SA</tex> и <tex>ISA</tex> могут быть улучшены за <tex>O(n)</tex>, чтобы отвечать на запросы вида |
− | * по данным подстрокам <tex>x</tex> и <tex>y</tex> строки <tex>T</tex> найти [ | + | * по данным подстрокам <tex>x</tex> и <tex>y</tex> строки <tex>T</tex> найти [[Алгоритм Касаи и др.| наибольший общий префикс]] <tex>lcp(x,y)</tex> и определить, какая из подстрок лексикографически меньше |
* по индексам <tex>i</tex> и <tex>j</tex> вычислить максимальный и минимальный суффикс в <tex>Suf[i,j]</tex> | * по индексам <tex>i</tex> и <tex>j</tex> вычислить максимальный и минимальный суффикс в <tex>Suf[i,j]</tex> | ||
− | Более того, такой <b>улучшенный суффиксный массив</b> может отвечать на запрос "по данным <tex>x,y</tex> {{---}} подстрокам <tex>T</tex> вычислить максимальное чило <tex>\alpha</tex>, такое, что <tex>x^{\alpha}</tex> является префиксом <tex>y</tex>" за константное время. Действительно, стоит заметить, что если <tex>x</tex> {{---}} префикс <tex>y = T[i | + | Более того, такой <b>улучшенный суффиксный массив</b> может отвечать на запрос "по данным <tex>x,y</tex> {{---}} подстрокам <tex>T</tex> вычислить максимальное чило <tex>\alpha</tex>, такое, что <tex>x^{\alpha}</tex> является префиксом <tex>y</tex>" за константное время. Действительно, стоит заметить, что если <tex>x</tex> {{---}} префикс <tex>y = T[i \ldots j]</tex>, то <tex>\alpha |x| \leqslant lcp(T[i \ldots j],T[i+|x| \ldots j] < (\alpha+1)|x|)</tex> |
Запросы к перевёрнутому улучшенному суфмассиву <tex>T^{R}</tex>также имеют смысл. С его помощью мы можем для пары <tex>x,y</tex> подстрок <tex>T</tex> найти их наибольший общий суффикс <tex>lcs(x,y)</tex> и наибольшее число <tex>\alpha</tex>, такое, что <tex>x^{\alpha}</tex> является суффиксом <tex>y</tex>. | Запросы к перевёрнутому улучшенному суфмассиву <tex>T^{R}</tex>также имеют смысл. С его помощью мы можем для пары <tex>x,y</tex> подстрок <tex>T</tex> найти их наибольший общий суффикс <tex>lcs(x,y)</tex> и наибольшее число <tex>\alpha</tex>, такое, что <tex>x^{\alpha}</tex> является суффиксом <tex>y</tex>. | ||
− | Возьмём строку <tex>T</tex> длины <tex>n</tex>. Для каждой позиции <tex>j</tex> мы выберем <tex>O( | + | Возьмём строку <tex>T</tex> длины <tex>n</tex>. Для каждой позиции <tex>j</tex> мы выберем <tex>O(\log{N})</tex> подстрок <tex>T[k \ldots j]</tex>, которые мы назовём каноническими. Определим как <tex>S^{l}_{j}</tex> <tex>l</tex>-ю кратчайшую каноническую подстроку, заканчивающуюся в позиции <tex>j</tex>. Для пары целых чисел <tex>1\leqslant i<j\leqslant n</tex> мы определим как <tex>\alpha(i,j)</tex> наибольшее <tex>l</tex>, такое, что <tex>S^{l}_{j}</tex> {{---}} суффикс <tex>T[i \ldots j]</tex>. |
Мы потребуем, чтобы канонические подстроки удовлетворяли определённым условиям: | Мы потребуем, чтобы канонические подстроки удовлетворяли определённым условиям: | ||
− | *<tex>S^{1}_{j} = T[j | + | *<tex>S^{1}_{j} = T[j \ldots j]</tex> и для некоторого <tex>l=O(\log{N})</tex> выполняется <tex>S^{l}_{j} = T[1 \ldots j]</tex> |
*<tex>\forall l:|S^{l+1}_{j}|\leqslant 2|S^{l}_{j}|</tex> | *<tex>\forall l:|S^{l+1}_{j}|\leqslant 2|S^{l}_{j}|</tex> | ||
*<tex>\alpha(i,j)</tex> и <tex>|S^{l}_{j}|</tex> можно вычислить за константное время для данных <tex>(i,j)</tex> и <tex>(i,l)</tex> соответственно | *<tex>\alpha(i,j)</tex> и <tex>|S^{l}_{j}|</tex> можно вычислить за константное время для данных <tex>(i,j)</tex> и <tex>(i,l)</tex> соответственно | ||
Такая структура данных работает при любом выборе канонических подстрок, которые удовлетворяют вышеприведённым условиям, например при простейшем <tex>|S^{l}_{j}| = \min(2^{l-1}, j)</tex> | Такая структура данных работает при любом выборе канонических подстрок, которые удовлетворяют вышеприведённым условиям, например при простейшем <tex>|S^{l}_{j}| = \min(2^{l-1}, j)</tex> | ||
+ | |||
{{Лемма | {{Лемма | ||
|id=lemma | |id=lemma | ||
+ | |author=0 | ||
+ | |statement= Пусть <tex>T[\mu \ldots j]</tex> - искомый лексикографически наименьший суффикс. Если у <tex>T[m \ldots j]</tex> нет непустых бордеров, то <tex>T[\mu \ldots j] = T[m \ldots j]</tex>. Иначе <tex>T[\mu \ldots j]</tex> {{---}} кратчайший непустой бордер <tex>T[m \ldots j]</tex>.<ref name="ref1">[http://starikovskaya.files.wordpress.com/2013/07/on-minimal-and-maximal-suffixes-of-a-substring.pdf On Minimal and Maximal Suffixes of a Substring]</ref> | ||
+ | |proof= Покажем, что <tex>T[\mu \ldots j]</tex> является одновременно префиксом и суффиксом <tex>T[m \ldots j]</tex>. Если <tex>T[m \ldots j]=T[\mu \ldots j]</tex>, то лемма доказана, иначе <tex>T[\mu \ldots j]\prec T[m \ldots j]</tex>. По определению лексикографического порядка, либо <tex>(1)</tex> <tex>T[\mu \ldots j]</tex> является префиксом <tex>T[m \ldots j]</tex>, либо <tex>(2)</tex> существует <tex>\displaystyle \ell<\min(|T[\mu \ldots j]|,\ |T[m \ldots j]|)</tex> такой, что <tex>T[\mu \ldots \mu+ \ell]=T[m \ldots m+\ell]</tex>, и <tex>T[\mu+\ell+1]<T[m+\ell+1]</tex>. | ||
+ | |||
+ | В случае <tex>(1)</tex> выполняется <tex> m<\mu</tex> и таким образом <tex>T[\mu \ldots j]</tex> также является суффиксом <tex>T[m \ldots j]</tex>. Покажем, что второй случай никогда не выполняется. Действительно, <tex>T[\mu \ldots ]\prec T[m \ldots ]</tex>, но в <tex>Suf [i,\ j]</tex> <tex>T[m \ldots ]</tex> является лексикографически наименьшим суффиксом. | ||
+ | |||
+ | Следовательно, <tex>T[\mu \ldots j]</tex> является одновременно префиксом и суффиксом <tex>T[m \ldots j]</tex>. Если <tex>T[m \ldots j]</tex> не имеет непустых бордеров, то <tex>\mu=m</tex>. Иначе, <tex>T[\mu \ldots j]</tex> является бордером <tex>T[m \ldots j]</tex>. Предположим, <tex>T[\mu \ldots j]</tex> - не наименьший непустой бордер <tex>T[m \ldots j]</tex>, тогда существует более короткий бордер <tex>\beta</tex>. По определению, <tex>\beta</tex> {{---}} префикс <tex>T[m \ldots j]</tex>, следовательно он также является префиксом <tex>T[\mu \ldots j]</tex>. Следовательно, <tex>\beta\prec T[\mu \ldots j]</tex>, что приводит нас к противоречию. | ||
+ | }} | ||
+ | |||
+ | {{Лемма | ||
+ | |id=lemma2 | ||
|author=1 | |author=1 | ||
− | |statement= Минимальный суффикс <tex>T[i | + | |statement= Минимальный суффикс <tex>T[i \ldots j]</tex> равен либо |
− | <tex>(a)\ T[p | + | <tex>(a)\ T[p \ldots j]</tex>, где <tex>p</tex> {{---}} начальная позиция минимального суффикса в <tex>Suf[i,j]</tex>, либо |
<tex>(b)</tex> минимальному суффиксу <tex>|S^{\alpha(i,j)}_{j}|</tex>. | <tex>(b)</tex> минимальному суффиксу <tex>|S^{\alpha(i,j)}_{j}|</tex>. | ||
Более того, <tex>p</tex> может быть найдено за константное время с использованием улучшенного суффиксного массива строки <tex>T</tex>. | Более того, <tex>p</tex> может быть найдено за константное время с использованием улучшенного суффиксного массива строки <tex>T</tex>. | ||
− | |proof= По | + | |proof= По лемме 0 минимальный суффикс равен либо <tex>T[p \ldots j]</tex>, либо его кратчайшему непустому бордеру. Более того, в последнем случае длина минимального суффикса равна не превышает <tex>\displaystyle \frac{1}{2}|T[p \ldots j]|\leqslant\frac{1}{2}|T[i \ldots j]|</tex>. С другой стороны, по второму свойству канонических подстрок, длина <tex>S_{j}^{\alpha(i,j)}</tex> равна как минимум <tex>\displaystyle \frac{1}{2}|T[i \ldots j]|</tex>. Таким образом, во втором случае минимальный суффикс <tex>T[i \ldots j]</tex> является минимальным суффиксом <tex>S_{j}^{\alpha(i,j)}</tex>. Заметим, что для <tex>i=j</tex> значения <tex>\alpha(i,\ j)</tex> не определены, но тогда выполняется случай <tex>(a)</tex> из условия леммы. Чтобы доказать финальное выражение, вспомним, что нахождение минимального суффикса <tex>Suf [i,\ j]</tex> {{---}} одна из базовых операций, поддерживаемых улучшенным суфмассивом. |
}} | }} | ||
Требуемая структура данных, помимо улучшенного суфмассива, должна, для каждого <tex>j=1,\ \ldots,\ n</tex> содержать битовый вектор <tex>B_{j}</tex> длиной <tex>\alpha(1,\ j)</tex>. Положим <tex>B_{j}[\ell]=1</tex> тогда и только тогда, когда минимальный суффикс <tex>S_{j}^{\ell}</tex> длиннее, чем <tex>|S_{j}^{\ell-1}|</tex>. Для <tex>\ell=1</tex> мы всегда считаем <tex>B_{j}[1]=1</tex>, поскольку <tex>S_{j}^{1}</tex> является минимальным суффиксом самого себя. Вспомним, что количество канонических подстрок для каждого <tex>j</tex> равна <tex>\mathcal{O}(\log n)</tex> , поэтому каждый <tex>B_{j}</tex> вмещается в константное количество машинных слов и структура данных занимает <tex>\mathcal{O}(n)</tex> памяти. | Требуемая структура данных, помимо улучшенного суфмассива, должна, для каждого <tex>j=1,\ \ldots,\ n</tex> содержать битовый вектор <tex>B_{j}</tex> длиной <tex>\alpha(1,\ j)</tex>. Положим <tex>B_{j}[\ell]=1</tex> тогда и только тогда, когда минимальный суффикс <tex>S_{j}^{\ell}</tex> длиннее, чем <tex>|S_{j}^{\ell-1}|</tex>. Для <tex>\ell=1</tex> мы всегда считаем <tex>B_{j}[1]=1</tex>, поскольку <tex>S_{j}^{1}</tex> является минимальным суффиксом самого себя. Вспомним, что количество канонических подстрок для каждого <tex>j</tex> равна <tex>\mathcal{O}(\log n)</tex> , поэтому каждый <tex>B_{j}</tex> вмещается в константное количество машинных слов и структура данных занимает <tex>\mathcal{O}(n)</tex> памяти. | ||
===Алгоритм запроса=== | ===Алгоритм запроса=== | ||
− | Предположим, что мы ищем минимальный суффикс <tex>T[i | + | Предположим, что мы ищем минимальный суффикс <tex>T[i \ldots j]</tex> c <tex>\alpha(i,\ j)=\ell</tex>. Наш подход основан на лемме 1. Если выполняется случай <tex>(a)</tex>, лемма позволяет нам вычислить ответ за <tex>\mathcal{O}(1)</tex>. В общем случае, мы найдём минимальный суффикс <tex>S_{j}^{\ell}</tex>, сравним его с <tex>T[p \ldots j]</tex> и вернём меньший из них. |
− | Мы используем | + | Мы используем лемму 1 и битовый вектор <tex>B_{j}</tex> чтобы посчитать минимальный суффикс <tex>S_{j}^{\ell}</tex>. Назовём <tex>\ell'</tex> наибольший индекс, не превышающий <tex>\ell</tex>, такой, что <tex>B_{j}[\ell']=1</tex>. Заметим, что такой индекс всегда существует (поскольку<tex> B_{j}[1]=1</tex>) и может быть найден за константное время с использованием битовых операций. Для любого индекса <tex>\ell''\in\{\ell'+1,\ \ldots,\ \ell\}</tex> мы имеем <tex>B_{j}[\ell'']=0</tex>, т.е., случай <tex>(b)</tex> леммы 1 выполняется для <tex>S_{j}^{\ell''}</tex>. Тогда, по индукции, минимальный суффикс <tex>S_{j}^{\ell}</tex> на самом деле является минимальным суффиксом <tex>S_{j}^{\ell'}</tex>. С другой стороны, <tex>B_{j}[\ell']=1</tex>, поэтому для последнего мы можем гарантировать, что выполняется первый случай леммы, что позволяет нам найти минимальный суффикс <tex>S_{j}^{\ell}</tex> за константное время. |
===Построение искомой структуры данных=== | ===Построение искомой структуры данных=== | ||
− | Простой алгоритм построения с временем работы <tex>\mathcal{O}(n\log n)</tex> также основывается на | + | Простой алгоритм построения с временем работы <tex>\mathcal{O}(n\log n)</tex> также основывается на лемме 1. Покажем, что построив улучшенный суфмассив, мы можем найти <tex>B_{j}</tex> за <tex>\mathcal{O}(\log n)</tex>. Мы ищем минимальный суффикс <tex>S_{j}^{\ell}</tex> для последовательных значений <tex>\ell</tex>. Как только мы получили результат <tex>\ell-1</tex>, случай <tex>(a)</tex> леммы 1 даёт нам второго кандидата на минимальный суффикс <tex>S_{j}^{\ell}</tex>, и наш улучшенный суфмассив позволяет нам выбрать наименьшего из этих двух кандидатов. Мы устанавливаем <tex>B_{j}[\ell]=1</tex> если меньший кандидат не содержится в <tex>S_{j}^{\ell-1}</tex>. Стало быть, мы получили следующий результат: |
{{Теорема | {{Теорема | ||
Строка 191: | Строка 213: | ||
Вышеописанная конструкция проста и работает для любого выбора канонических подстрок, но, к сожалению, она не может быть использована для достижения компромисса между временем запроса и временем построения. Далее мы предложим особый способ выбора канонических подстрок и опишем альтернативный метод построения. Этот способ основывается на предположении, что по данной строке длины <tex>k</tex> мы можем найти минимальный суффикс для всех её префиксов за <tex>\mathcal{O}(k)</tex>. Следовательно, нам удобно иметь много <tex>S_{j}^{\ell}</tex>, которые являются префиксами друг друга. Тогда, естественным будет выбрать <tex>|S_{j}^{\ell}|=2^{\ell-1}+(j\ mod\ 2^{\ell-1})</tex> , поскольку все подстроки <tex>S_{\alpha 2^{l-1}}^{\ell},\ S_{\alpha 2^{l-1}+1}^{\ell},\ \ldots,\ S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex> являются префиксами <tex>S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex>. К сожалению, подстроки, выбранные таким способом, не удовлетворяют условию <tex>|S_{j}^{\ell}|\leqslant 2|S_{j}^{\ell-1}|</tex>, и, посему, нам необходимо немного изменить его. | Вышеописанная конструкция проста и работает для любого выбора канонических подстрок, но, к сожалению, она не может быть использована для достижения компромисса между временем запроса и временем построения. Далее мы предложим особый способ выбора канонических подстрок и опишем альтернативный метод построения. Этот способ основывается на предположении, что по данной строке длины <tex>k</tex> мы можем найти минимальный суффикс для всех её префиксов за <tex>\mathcal{O}(k)</tex>. Следовательно, нам удобно иметь много <tex>S_{j}^{\ell}</tex>, которые являются префиксами друг друга. Тогда, естественным будет выбрать <tex>|S_{j}^{\ell}|=2^{\ell-1}+(j\ mod\ 2^{\ell-1})</tex> , поскольку все подстроки <tex>S_{\alpha 2^{l-1}}^{\ell},\ S_{\alpha 2^{l-1}+1}^{\ell},\ \ldots,\ S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex> являются префиксами <tex>S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex>. К сожалению, подстроки, выбранные таким способом, не удовлетворяют условию <tex>|S_{j}^{\ell}|\leqslant 2|S_{j}^{\ell-1}|</tex>, и, посему, нам необходимо немного изменить его. | ||
− | Для <tex>\ell=1</tex> мы определим <tex>S_{j}^{1}=T[j | + | Для <tex>\ell=1</tex> мы определим <tex>S_{j}^{1}=T[j \ldots j]</tex>. Для <tex>\ell>1</tex> установим <tex>m=\lfloor\ell/2\rfloor-1</tex> и определим <tex>S_{j}^{\ell}</tex> таким образом: |
<tex> | <tex> | ||
|S_{j}^{\ell}|= | |S_{j}^{\ell}|= | ||
Строка 199: | Строка 221: | ||
\end{cases}</tex> | \end{cases}</tex> | ||
− | Заметим, что если <tex>2 \cdot 2^{m}\leqslant j<3\cdot 2^{m}</tex>, то <tex>T[1 | + | Заметим, что если <tex>2 \cdot 2^{m}\leqslant j<3\cdot 2^{m}</tex>, то <tex>T[1 \ldots j]=S_{j}^{2m+2}</tex>, в то время как, если <tex>3 \cdot 2^{m}\leqslant j<4\cdot 2^{m}</tex>, то <tex>T[1 \ldots j]=S_{j}^{2m+3}</tex>. Очевидно, что количество таких подстрок, заканчивающихся в <tex>j</tex> получается <tex>\mathcal{O}(\log n)</tex>. Докажем далее, что канонические подстроки, выбранные вышеуказанным способом, имеют необходимые свойства. |
{{Утверждение | {{Утверждение | ||
Строка 213: | Строка 235: | ||
|author=4 | |author=4 | ||
|statement= Для <tex>1\leqslant i<j\leqslant n</tex>, величина <tex>\alpha(i,\ j)</tex> может быть посчитана за константное время. | |statement= Для <tex>1\leqslant i<j\leqslant n</tex>, величина <tex>\alpha(i,\ j)</tex> может быть посчитана за константное время. | ||
− | |proof= Положим <tex> m=\lfloor\log|T[i | + | |proof= Положим <tex> m=\lfloor\log|T[i \ldots j]|\rfloor</tex>. Заметим, что |
− | <tex>|S_{j}^{2m-1}|=3\cdot 2^{m-2}+(j \ mod \ 2^{m-2})<2^{m}\leqslant|T[i | + | <tex>|S_{j}^{2m-1}|=3\cdot 2^{m-2}+(j \ mod \ 2^{m-2})<2^{m}\leqslant|T[i \ldots j]|</tex> |
− | <tex>|S_{j}^{2m+2}|=2\cdot 2^{m}+(j \ mod \ 2^{m})\geqslant 2^{m+1}>|T[i | + | <tex>|S_{j}^{2m+2}|=2\cdot 2^{m}+(j \ mod \ 2^{m})\geqslant 2^{m+1}>|T[i \ldots j]|</tex>. |
Таким образом, <tex>\alpha(i,\ j)\in\{2m-1,2m,\ 2m+1\}</tex>, и мы можем за константное время проверить, какое из этих трёх значений корректно. | Таким образом, <tex>\alpha(i,\ j)\in\{2m-1,2m,\ 2m+1\}</tex>, и мы можем за константное время проверить, какое из этих трёх значений корректно. | ||
}} | }} | ||
− | После построения улучшенного суфмассива, мы установили все биты<tex>B_{j}[1]</tex> в 1. После этого, для каждого <tex>\ell>1</tex> мы посчитали минимальные суффиксы подстрок <tex>S_{j}^{\ell}</tex>, как указано далее. Зафиксируем <tex>\ell>1</tex> и разобьём <tex>T</tex> на куски размером <tex>2^{m}</tex>(где <tex>m=\lfloor\ell/2\rfloor-1</tex>) . Теперь каждый <tex>S_{j}^{\ell}</tex> является префиксом конкатенации максимум 4х таких кусков. | + | После построения улучшенного суфмассива, мы установили все биты<tex>B_{j}[1]</tex> в 1. После этого, для каждого <tex>\ell>1</tex> мы посчитали минимальные суффиксы подстрок <tex>S_{j}^{\ell}</tex>, как указано далее. Зафиксируем <tex>\ell>1</tex> и разобьём <tex>T</tex> на куски размером <tex>2^{m}</tex>(где <tex>m=\lfloor\ell/2\rfloor-1</tex>) . Теперь каждый <tex>S_{j}^{\ell}</tex> является префиксом конкатенации максимум 4х таких кусков. Известно, что по данной строке можно посчитать длины минимальных суффиксов всех её префиксов за линейное время с помощью одной из вариаций алгоритма Дюваля<ref name="ref2">[http://ge.tt/api/1/files/5uKJjWQ/0/blob?download Factorizing words over an ordered alphabet]</ref>. Разделим <tex>T</tex> на куски длиной <tex>2^{m}</tex>(где <tex>m=\lfloor\ell/2\rfloor-1</tex>) и запустим этот алгоритм для каждых четырёх (или менее, в конце) последовательных кусков. Это даст нам минимальные суффиксы <tex>S_{j}^{\ell}</tex> для всех <tex>1\leqslant j\leqslant n</tex>, за время <tex>\mathcal{O}(n)</tex>. Значение <tex>B_{j}[\ell]</tex> определено с помощью сравнения длины вычисленного минимального суффикса <tex>S_{j}^{\ell}</tex> с <tex>|S_{j}^{\ell-1}|</tex>. У нас <tex>\mathcal{O}(\log n)</tex> фаз алгоритма, что даёт нам время <tex>\mathcal{O}(n\log n)</tex> и <tex>\mathcal{O}(n)</tex> требуемой памяти. |
===Компромисс=== | ===Компромисс=== | ||
Чтобы получить структуру данных, со временем построения <tex>\mathcal{O}(n\log n/\tau)</tex> и временем запроса <tex>\mathcal{O}(\tau)</tex>, мы немного по-другому определим битовые вектора. Положим <tex>B_{j}</tex> размером <tex>\lfloor\alpha(1,\ j)/\tau\rfloor</tex>, притом <tex>B_{j}[k]=1</tex> тогда и только тогда, когда <tex>k=1</tex> или минимальный суффикс <tex>S_{j}^{\tau k}</tex> длиннее, чем <tex>|S_{j}^{\tau(k-1)}|</tex>. В этом случае, нам необходимо только <tex>\mathcal{O}(\log n/\tau)</tex> фаз в алгоритме построения, поэтому он займёт <tex>\mathcal{O}(n\log n/\tau)</tex> времени. | Чтобы получить структуру данных, со временем построения <tex>\mathcal{O}(n\log n/\tau)</tex> и временем запроса <tex>\mathcal{O}(\tau)</tex>, мы немного по-другому определим битовые вектора. Положим <tex>B_{j}</tex> размером <tex>\lfloor\alpha(1,\ j)/\tau\rfloor</tex>, притом <tex>B_{j}[k]=1</tex> тогда и только тогда, когда <tex>k=1</tex> или минимальный суффикс <tex>S_{j}^{\tau k}</tex> длиннее, чем <tex>|S_{j}^{\tau(k-1)}|</tex>. В этом случае, нам необходимо только <tex>\mathcal{O}(\log n/\tau)</tex> фаз в алгоритме построения, поэтому он займёт <tex>\mathcal{O}(n\log n/\tau)</tex> времени. | ||
− | Как и ранее, предположим, что мы ищем минимальный суффикс <tex>T[i | + | Как и ранее, предположим, что мы ищем минимальный суффикс <tex>T[i \ldots j]</tex>, при <tex>\alpha(i,\ j)=\ell</tex>. Самое сложное в этом {{---}} найти минимальный суффикс <tex>S_{j}^{\ell}</tex>, и далее необходимо найти <tex>\ell'\leqslant\ell</tex>, такой, что минимальный суффикс <tex>S_{j}^{\ell}</tex> на самом деле является минимальным суффиксом <tex>S_{j}^{\ell'}</tex>, но длиннее, чем <tex>|S_{j}^{\ell'-1}|</tex>. Если <tex>\ell=\tau k</tex> для целого <tex>k</tex>, мы можем найти наибольший <tex>k'\leqslant k</tex>, такой, что <tex>B[k']=1</tex>, и нам будет известно, что <tex>\ell'\in(\tau(k'-1),\ \tau k']</tex>. В общем случае, мы выберем наибольший <tex>k</tex>, такой что <tex>\tau k\leqslant\ell</tex>, и будем знать, что мы должны рассмотреть <tex>\ell'\in(\tau k,\ \ell]</tex> и <tex> \ell'\in(\tau(k'-1),\ \tau k']</tex>, где <tex>k'</tex> определён, как в предыдущем случае. В результате, мы имеем <tex>\mathcal{O}(\tau)</tex> возможных значений <tex>\ell'</tex>, и нам известно, что искомый суффикс может быть найден, используя случай <tex>(a)</tex> леммы 1 для <tex>S_{j}^{\ell'}</tex> для каждого из этих значений. Тогда мы просто сгенерируем всех этих кандидатов и используем улучшенный суфмассив, чтобы найти наименьший суффикс среди них. В результате, запрос к нашей структуре данных будет выполняться за <tex>\mathcal{O}(\tau)</tex>. |
+ | |||
+ | '''Из вышеописанного следует теорема:''' | ||
{{Теорема | {{Теорема | ||
|author=5 | |author=5 | ||
|statement= | |statement= | ||
− | Для любого <tex>1\leqslant\tau\leqslant\log n</tex>, строка <tex>T</tex> длиной <tex>n</tex> может храниться в структуре данных, занимающей <tex>\mathcal{O}(n)</tex> памяти, позволяющей вычислять минимальный суффикс любой из подстрок <tex>T</tex> за время <tex>\mathcal{O}(\tau)</tex>. Такая структура данных может быть построена за <tex>\mathcal{O}(n\log n/\tau)</tex>. | + | Для любого <tex>1\leqslant\tau\leqslant\log n</tex>, строка <tex>T</tex> длиной <tex>n</tex> может храниться в структуре данных, занимающей <tex>\mathcal{O}(n)</tex> памяти, позволяющей вычислять минимальный суффикс любой из подстрок <tex>T</tex> за время <tex>\mathcal{O}(\tau)</tex>. Такая структура данных может быть построена за <tex>\mathcal{O}(n\log{n/\tau})</tex>. |
}} | }} | ||
Строка 247: | Строка 271: | ||
|author=7 | |author=7 | ||
|id=lemma | |id=lemma | ||
− | |statement= Рассмотрим подстроку <tex>T[i | + | |statement= Рассмотрим подстроку <tex>T[i \ldots j]</tex>. Используя улучшенный суффиксный массив строки <tex>T</tex>, за <tex>\mathcal{O}(1)</tex> времени можно найти такой индекс <tex>p\ (i\leqslant p\leqslant j)</tex>, что максимальный суффикс <tex>T[\mu \ldots j]</tex> строки <tex>T[i \ldots j]</tex> равен либо |
− | <tex>(a) T[p | + | <tex>(a) T[p \ldots j]</tex>, либо |
<tex>(b)</tex> максимальному суффиксу строки <tex>S_{j}^{\alpha(i,j)}</tex> | <tex>(b)</tex> максимальному суффиксу строки <tex>S_{j}^{\alpha(i,j)}</tex> | ||
|proof= | |proof= | ||
Строка 263: | Строка 287: | ||
|statement= Строка <tex>T</tex> длины <tex>n</tex> может храниться в структуре данных с <tex>\mathcal{O}(n)</tex> памяти, которая позволяет вычислять максимальный суффикс любой подстроки строки <tex>T</tex> за время <tex>\mathcal{O}(1)</tex>. | |statement= Строка <tex>T</tex> длины <tex>n</tex> может храниться в структуре данных с <tex>\mathcal{O}(n)</tex> памяти, которая позволяет вычислять максимальный суффикс любой подстроки строки <tex>T</tex> за время <tex>\mathcal{O}(1)</tex>. | ||
|proof= | |proof= | ||
− | Алгоритмы построения за <tex>\mathcal{O}(n\log n)</tex> и компромисс между временем запросов и временем построения, описанные [[#Построение искомой структуры данных|здесь]] и [[#Компромисс|здесь]] соответственно, также легко адаптируются к нашей задаче. В случае поиска максимального суффикса, тем не менее, мы можем добиться времени построения <tex>\mathcal{O}(n)</tex>, как будет показано | + | Алгоритмы построения за <tex>\mathcal{O}(n\log n)</tex> и компромисс между временем запросов и временем построения, описанные [[#Построение искомой структуры данных|здесь]] и [[#Компромисс|здесь]] соответственно, также легко адаптируются к нашей задаче. В случае поиска максимального суффикса, тем не менее, мы можем добиться времени построения <tex>\mathcal{O}(n)</tex>, как будет показано ниже. |
}} | }} | ||
Строка 269: | Строка 293: | ||
Ниже мы описываем алгоритм, работающий за константное время, который возвращает позицию <tex>p\in[i,\ j]</tex>. | Ниже мы описываем алгоритм, работающий за константное время, который возвращает позицию <tex>p\in[i,\ j]</tex>. | ||
− | Заметим, что если максимальный суффикс <tex>T[\mu | + | Заметим, что если максимальный суффикс <tex>T[\mu \ldots j]</tex> of <tex>T[i \ldots j]</tex> короче, чем <tex>S_{j}^{\alpha(i,j)}</tex> (случай (b) леммы 7), алгоритм может вернуть любое <tex>p\in[i,\ j]</tex>. Далее мы предполагаем, что <tex>T[\mu \ldots j]</tex> длиннее, чем <tex>S_{j}^{\alpha(i,\ j)}</tex> и показываем, что при этом предположении алгоритм вернёт <tex> p=\mu</tex>. Из нашего предположения свойств канонических подстрок следует, что <tex>\mu\in[i,\ r]</tex>, where <tex>r=j-|S_{j}^{\alpha(i,\ j)}|</tex>, и что длины суффиксов подстроки <tex>T[i \ldots j]</tex>, начинающихся с позиций в промежутке <tex>[i,\ r]</tex>, отличаются не более чем в два раза. |
Мы начнем со вспомогательной леммы, которая обозначалась как лемма 2 в <ref name="ref1"/> | Мы начнем со вспомогательной леммы, которая обозначалась как лемма 2 в <ref name="ref1"/> | ||
Строка 276: | Строка 300: | ||
|id=lemma | |id=lemma | ||
|author=9 | |author=9 | ||
− | |statement= Пусть <tex>P_{1}=T[p_{1} | + | |statement= Пусть <tex>P_{1}=T[p_{1} \ldots j]</tex> {{---}} префикс строки <tex>T[\mu \ldots j]</tex> и пусть <tex>P_{2}=T[p_{2} \ldots j]</tex>, где <tex>T[p_{2} \ldots j]</tex> {{---}} максимальный суффикс в <tex>Suf [i,\ p_{1}-1]</tex>. Если <tex>P_{1}</tex> не является префиксом <tex>P_{2}</tex>, тогда <tex>\mu=p_{1}</tex>. Иначе, <tex>P_{2}</tex> также является префиксом строки <tex>T[\mu \ldots j]</tex>. |
− | |proof= Пусть <tex>T[p_{1} | + | |proof= Пусть <tex>T[p_{1} \ldots ]</tex> {{---}} максимальный суффикс в <tex>Suf [i,\ r]</tex> и <tex>T[p_{2} \ldots ]</tex> {{---}} максимальный суффикс в <tex>Suf [i,\ p_{1}-1]</tex>. Очевидно, <tex>P_{1}=T[p_{1} \ldots j]</tex> является префиксом строки <tex>T[\mu \ldots j]</tex>. Предположим, что <tex>P_{1}</tex> {{---}} префикс <tex>P_{2}</tex> (иначе <tex>p_{1}=\mu\</tex> по лемме 9). Длины <tex>P_{1}</tex> и <tex>P_{2}</tex> различаются не более чем в два раза, поэтому <tex>2|P_{1}|\geqslant|P_{2}|</tex>. Благодаря этому, <tex>P_{1}</tex> и <tex>P_{2}</tex> имеют некоторые интересные свойства, описанные в последующих леммах. Эти леммы по существу повторяют леммы 4 и 5 из <ref name="ref1" />, но здесь мы приводим доказательства вследствие другого обозначения. |
}} | }} | ||
Строка 284: | Строка 308: | ||
|id=lemma | |id=lemma | ||
|author=10 | |author=10 | ||
− | |statement= Подстрока <tex>\rho=T[p_{2} | + | |statement= Подстрока <tex>\rho=T[p_{2} \ldots p_{1}-1]</tex> {{---}} минимальный период строки <tex>P_{2}</tex>, т.е. <tex>\rho</tex> {{---}} кратчайшая строка, такая, что для какого-то <tex>s\geqslant 1</tex> выполняется <tex>P_{2}=\rho^{s}\rho'</tex>. |
− | |proof= Поскольку <tex>P_{1}</tex> {{---}} бордер <tex>P_{2},\ \rho=T[p_{2} | + | |proof= Поскольку <tex>P_{1}</tex> {{---}} бордер <tex>P_{2},\ \rho=T[p_{2} \ldots p_{1}-1]</tex> {{---}} период <tex>P_{2}</tex>. Остается лишь доказать, что невозможно найти более короткий период. Таким образом, рассмотрим(consider) кратчайший период <tex>\gamma</tex>, и предположим, что <tex>|\gamma|<|\rho|</tex>. Тогда <tex>|\gamma|+|\rho|\leqslant 2|\rho|\leqslant|T[p_{2} \ldots j]|</tex>, и по лемме о периодичности подстрока <tex>P_{2}</tex> имеет другой период <tex>\mathrm{g}\mathrm{c}\mathrm{d}(|\gamma|,\ |\rho|)</tex> . Так как <tex>\gamma</tex> {{---}} кратчайший период, то <tex>|\rho|</tex> должно быть степенью <tex>|\gamma|</tex>, т.е. <tex>\rho=\gamma^{k}</tex> для какого-то <tex>k\geqslant 2</tex>. |
− | Предположим, что <tex>T[p_{1} | + | Предположим, что <tex>T[p_{1} \ldots ]\prec\gamma T[p_{1} \ldots ]</tex> '''(лексикографически меньше)'''. Тогда приписывание к обеим частям последнего неравенства степеней <tex>\gamma</tex> даёт <tex>\gamma^{\ell-1}T[p_{1} \ldots ]\prec\gamma^{\ell}T[p_{1} \ldots ]</tex> для любого <tex>1\leqslant\ell\leqslant k</tex>, таким образом из транзитивности <tex>\prec</tex> получаем <tex>T[p_{1} \ldots ]\prec\gamma^{k}T[p_{1} \ldots ]=T[p_{2} \ldots ]</tex>, что противоречит максимальности <tex>T[p_{1} \ldots ]</tex> in <tex>Suf[i,\ r]</tex>. Таким образом, <tex>T[p_{1} \ldots ]\succ\gamma T[p_{1} \ldots ]</tex>, и следовательно <tex>\gamma^{k-1}T[p_{1} \ldots ]\succ\gamma^{k}T[p_{1} \ldots ]</tex>. Но <tex>\gamma^{k-1}T[p_{1} \ldots ]=T[p_{2}+|\gamma| \ldots ]</tex> и <tex>\gamma^{k}T[p_{1} \ldots ]=T[p_{2} \ldots ]</tex>, поэтому <tex>T[p_{2}+|\gamma| \ldots ]</tex> больше, чем <tex>T[p_{2} \ldots ]</tex> и принадлежит <tex>Suf [i,\ p_{1}-1]</tex>, противоречие. Заметим, что на самом деле <tex>s\geqslant 2</tex>, потому что <tex>2|P_{1}|\geqslant|P_{2}|=|P_{1}|+|\rho|</tex>, поэтому <tex>|P_{1}|\geqslant|\rho|.\ </tex> |
}} | }} | ||
Строка 293: | Строка 317: | ||
|id=lemma | |id=lemma | ||
|author=11 | |author=11 | ||
− | |statement= Положим, <tex>P_{2}=\rho P_{1}=\rho^{s}\rho'</tex>. Тогда максимальный суффикс <tex>T[\mu | + | |statement= Положим, <tex>P_{2}=\rho P_{1}=\rho^{s}\rho'</tex>. Тогда максимальный суффикс <tex>T[\mu \ldots j]</tex> {{---}} длиннейший суффикс строки <tex>T[i \ldots j]</tex> равен <tex>\rho^{t}\rho'</tex> для некоторого <tex>t</tex>. (См. рисунок 1) |
− | |proof= Очевидно, что <tex>P_{2}</tex> является бордером <tex>T[\mu | + | |proof= Очевидно, что <tex>P_{2}</tex> является бордером <tex>T[\mu \ldots j]</tex>. Из <tex>P_{2}=\rho P_{1}</tex> и <tex>|T[\mu \ldots j]|\leqslant 2|P_{1}|</tex> имеем <tex>|T[\mu \ldots j]|+|\rho|\leqslant 2|P_{1}|+\rho\leqslant 2|P_{2}|</tex>. Следовательно вхождения <tex>P_{2}</tex> в качестве префикса и в качестве суффикса строки <tex>T[\mu \ldots j]</tex> перекрывают друг друга как минимум в <tex>|\rho|</tex> позициях. Т.к. <tex>|\rho|</tex> {{---}} период <tex>P_{2}</tex>, отсюда следует, что <tex>|\rho|</tex> также является периодом <tex>T[\mu \ldots j]</tex>. Таким образом, <tex>T[\mu \ldots j]=\rho''\rho^{r}\rho'</tex>, где <tex>r</tex> {{---}} целое число и <tex>\rho''</tex> {{---}} суффикс <tex>\rho</tex>. Более того, <tex>\rho^{2}</tex> {{---}} это префикс <tex>T[\mu \ldots j]</tex>, поскольку является префиксом <tex>P_{2}</tex>, который в свою очередь является префиксом <tex>T[\mu \ldots j]</tex>. Теперь <tex>\rho''\neq\xi j</tex> будет означать нетривиальное вхождение <tex>\rho</tex> в <tex>\rho^{2}</tex>, которое противоречит примитивности <tex>\rho</tex>, смотри <ref name="ref4">[http://www.google.ru/books?hl=en&lr=&id=PuOOY_DR55UC&oi=fnd&pg=PR7&dq=M.+Crochemore,+C.+Hancart,+and+T.+Lecroq.+Algorithms+on+Strings.+Cambridge+University+Press,+2007&ots=oe_VacDwgA&sig=PKoDRn6K6nZsWfajL0-0jkSlAf8&redir_esc=y#v=onepage&q&f=false M. Crochemore, C. Hancart, and T. Lecroq. Algorithms on Strings. Cambridge University Press, 2007]</ref>. |
[[Файл:image001.png|Рис. 1. Схематичная иллюстрация к лемме 11.|800px]] | [[Файл:image001.png|Рис. 1. Схематичная иллюстрация к лемме 11.|800px]] | ||
Строка 301: | Строка 325: | ||
− | Таким образом, <tex>T[\mu | + | Таким образом, <tex>T[\mu \ldots j]=\rho^{r}\rho'</tex>. Если <tex>t>r</tex>, тогда <tex>\rho^{t}\rho'\succ\rho^{r}\rho'</tex>, поэтому <tex>T[\mu \ldots j]</tex> {{---}} длиннейший суффикс строки <tex>T[i \ldots j]</tex>, равный <tex>\rho^{t}\rho'</tex> для некоторого <tex>t</tex>. |
}} | }} | ||
'''Доказательство леммы 7''' | '''Доказательство леммы 7''' | ||
Строка 307: | Строка 331: | ||
<tex>\triangleright</tex> | <tex>\triangleright</tex> | ||
− | Пусть <tex>T[p_{1} | + | Пусть <tex>T[p_{1} \ldots ]</tex> {{---}} максимальный суффикс в <tex>Suf [i,\ r]</tex> и <tex>T[p_{2} \ldots ]</tex> {{---}} максимальный суффикс в <tex>Suf [i,\ p_{1}-1]</tex>. Сначала вычислим <tex>p_{1}</tex> и <tex>p_{2}</tex> за <tex>\mathcal{O}(1)</tex>, используя улучшенный суффиксный массив. Затем проверим, что <tex>T[p_{1} \ldots j]</tex> является префиксом <tex>T[p_{2} \ldots j]</tex>. Если это неверно, то мы возвращаем <tex>p=p_{1}</tex>. Иначе, мы вычисляем максимальное целое число <tex>r</tex>, такое, что <tex>\rho^{r}</tex> (для <tex>\rho=T[p_{2} \ldots p_{1}-1])</tex>) {{---}} суффикс <tex>T[i \ldots p_{1}-1]</tex>, используя метод из алгоритма поиска минимального суффикса, и возвращаем <tex>p=p_{1}-r|\rho|</tex>. Из вышеописанных лемм следует, что если <tex>T[\mu \ldots j]</tex> длиннее <tex>S_{j}^{\alpha(i,\ j)}</tex>, тогда <tex> p=\mu</tex>. |
<tex>\triangleleft</tex> | <tex>\triangleleft</tex> | ||
Строка 313: | Строка 337: | ||
===Построение структуры данных=== | ===Построение структуры данных=== | ||
− | Наш алгоритм основывается на следующем понятии. Для <tex>1\leqslant p\leqslant j\leqslant n</tex> мы говорим, что позиция <tex>p</tex> <tex>j</tex>-активна, если не существует такой позиции <tex>p'\in\{p+1,\ \ldots,\ j\}</tex>, что <tex>T[p' | + | Наш алгоритм основывается на следующем понятии. Для <tex>1\leqslant p\leqslant j\leqslant n</tex> мы говорим, что позиция <tex>p</tex> <tex>j</tex>-активна, если не существует такой позиции <tex>p'\in\{p+1,\ \ldots,\ j\}</tex>, что <tex>T[p' \ldots j]\succ T[p \ldots j]</tex>. Другими словами, <tex>p</tex> {{---}} <tex>j</tex>-активна тогда и только тогда, когда <tex>T[p \ldots j]</tex> {{---}} максимальный суффикс самого себя. |
− | Максимальный суффикс любой строки является своим собственным максимальным суффиксом, поэтому из определения следует, что стартовая позиция максимального суффикса строки <tex>T[i | + | Максимальный суффикс любой строки является своим собственным максимальным суффиксом, поэтому из определения следует, что стартовая позиция максимального суффикса строки <tex>T[i \ldots j]</tex> {{---}} это минимальная <tex>j</tex>-активная позиция в промежутке <tex>[i,\ j]</tex>. Следовательно, при <tex>\ell>1</tex> мы имеем <tex>B_{j}[\ell]=1</tex> тогда и только тогда, когда существует как минимум одна <tex>j</tex>-активная позиция в диапазоне <tex>R_{j}^{\ell}=[j-|S_{j}^{\ell}|+1,\ j-|S_{j}^{\ell-1}|]</tex>. Положим <tex>R_{j}^{1}=[j,\ j]</tex>, тогда это равенство также сохраняется для <tex>\ell=1</tex> (поскольку <tex>j</tex> всегда <tex>j</tex>-активна) |
− | '''''Пример (12):''''' Если <tex>T[1 | + | '''''Пример (12):''''' Если <tex>T[1 \ldots 8]=dcccabab</tex>, то 8-активными позициями будут <tex>1,\ 2,\ 3,\ 4,\ 6,\ 8.</tex> |
− | Алгоритм построения перебирает <tex>j</tex> от <tex>1</tex> до <tex>n</tex>, сохраняя список активных позиций и вычисляя битовые вектора <tex>B_{j}</tex>. Мы также сохраняем диапазоны <tex>R_{j}^{\ell}</tex> для выбора канонических подстрок, описанных [[#Построение искомой структуры данных|здесь]], которые образуют разбиение <tex>[ | + | Алгоритм построения перебирает <tex>j</tex> от <tex>1</tex> до <tex>n</tex>, сохраняя список активных позиций и вычисляя битовые вектора <tex>B_{j}</tex>. Мы также сохраняем диапазоны <tex>R_{j}^{\ell}</tex> для выбора канонических подстрок, описанных [[#Построение искомой структуры данных|здесь]], которые образуют разбиение <tex>[1,\ j]</tex>. Два следующих результата описывают изменения списка <tex>j</tex>-активных позиций и диапазонов <tex>R_{j}^{\ell}</tex>, когда мы увеличиваем <tex>j</tex>. |
{{Лемма | {{Лемма | ||
Строка 325: | Строка 349: | ||
|statement= Если список всех <tex>(j-1)</tex>-активных позиций состоит из <tex>p_{1}<p_{2}<</tex> . . . <tex><p_{k}</tex>, то список <tex>j</tex>-активных позиций может быть создан путём добавления <tex>j</tex>, и повторения следующей процедуры: если <tex>p_{\ell}</tex> и <tex>p_{\ell+1}</tex> {{---}} два соседа в текущем списке и <tex>T[j]\neq T[j+p_{\ell}-p_{\ell+1}]</tex>, удаляем <tex>p_{\ell}</tex> или <tex>p_{\ell+1}</tex> из списка, зависимо от того, что <tex>T[j]>T[j+p_{\ell}-p_{\ell+1}]</tex> или <tex>T[j]<T[j+p_{\ell}-p_{\ell+1}]</tex>, соответственно. | |statement= Если список всех <tex>(j-1)</tex>-активных позиций состоит из <tex>p_{1}<p_{2}<</tex> . . . <tex><p_{k}</tex>, то список <tex>j</tex>-активных позиций может быть создан путём добавления <tex>j</tex>, и повторения следующей процедуры: если <tex>p_{\ell}</tex> и <tex>p_{\ell+1}</tex> {{---}} два соседа в текущем списке и <tex>T[j]\neq T[j+p_{\ell}-p_{\ell+1}]</tex>, удаляем <tex>p_{\ell}</tex> или <tex>p_{\ell+1}</tex> из списка, зависимо от того, что <tex>T[j]>T[j+p_{\ell}-p_{\ell+1}]</tex> или <tex>T[j]<T[j+p_{\ell}-p_{\ell+1}]</tex>, соответственно. | ||
− | |proof= Сначала мы докажем, что если позиция <tex>1\leqslant p\leqslant j-1</tex> не является <tex>(j-1)</tex>-активной, то она также не является и <tex>j</tex>-активной. Действительно, если <tex>p</tex> не <tex>(j-1)</tex>-активна, тогда по определению существует позиция <tex>p<p'\leqslant j-1</tex>, такая, что <tex>T[p | + | |proof= Сначала мы докажем, что если позиция <tex>1\leqslant p\leqslant j-1</tex> не является <tex>(j-1)</tex>-активной, то она также не является и <tex>j</tex>-активной. Действительно, если <tex>p</tex> не <tex>(j-1)</tex>-активна, тогда по определению существует позиция <tex>p<p'\leqslant j-1</tex>, такая, что <tex>T[p \ldots j-1]\prec T[p' \ldots j-1]</tex>. Следовательно, <tex>T[p \ldots j]=T[p \ldots j-1]T[j]\prec T[p' \ldots j-1]T[j]=T[p' \ldots j]</tex> и <tex>p</tex> не является <tex>j</tex>-активной. Отсюда, единственными кандидатами на <tex>j</tex>-активные позиции остаются <tex>(j-1)</tex>-активные позиции и <tex>j</tex>. |
− | Далее, заметим, что если <tex>1\leqslant p\leqslant j-1</tex> {{---}} <tex>\mathrm{a}(j-1)</tex>-активная позиция и <tex>T[p' | + | Далее, заметим, что если <tex>1\leqslant p\leqslant j-1</tex> {{---}} <tex>\mathrm{a}(j-1)</tex>-активная позиция и <tex>T[p' \ldots j-1]</tex> {{---}} префикс <tex>T[p \ldots j-1]</tex>, то <tex>p'</tex> тоже является <tex>(j-1)</tex>-активной. Если это не так, тогда существует позиция <tex>p'',\ p'<p''<j-1</tex>, такая, что <tex>T[p' \ldots j-1]\prec T[p'' \ldots j-1]</tex>, и из этого следует, что <tex>T[p \ldots j-1]=T[p' \ldots j-1]T[j+p-p' \ldots j-1]\prec T[p'' \ldots j-1]</tex>, противоречие. <tex>\mathrm{A}(j-1)</tex>-активная позиция <tex>p</tex> не является <tex>j</tex>-активной только если (1) <tex>T[j]\geqslant T[p]</tex> или (2) существует <tex>p<p'\leqslant j-1</tex> такая, что <tex>T[p' \ldots j-1]</tex> {{---}} префикс <tex>T[p \ldots j-1]</tex>, т.е. <tex>p'</tex> {{---}} <tex>(j-1)</tex>-активна и <tex>T[p' \ldots j]\succ T[p \ldots j]</tex> или, другими словами, <tex>T[j]>T[j+p-p']</tex>. Оба этих случая выявляются процедурой удаления. |
}} | }} | ||
− | '''''Пример (14):''''' Если <tex>T[1 | + | '''''Пример (14):''''' Если <tex>T[1 \ldots 9]=dcccababb</tex>, то 8-активными позициями будут: <tex>1,\ 2,\ 3,\ 4,\ 6,\ 8,</tex> и 9-активными позициями будут: <tex>1,\ 2,\ 3,\ 4,\ 8,\ 9,</tex> т.е. мы добавляем <tex>9</tex> в наш список 8-активных позиций, а затем удаляем <tex>6</tex>. |
{{Утверждение | {{Утверждение | ||
Строка 371: | Строка 395: | ||
− | Мы просматриваем позиции строки <tex>T</tex> слева направо, вычисляя битовые вектора. Мы сохраняем список активных позиций и разбиение <tex>[ | + | Мы просматриваем позиции строки <tex>T</tex> слева направо, вычисляя битовые вектора. Мы сохраняем список активных позиций и разбиение <tex>[1,\ j]</tex> на диапазоны <tex>R_{j}^{\ell}</tex>. Кроме того, для каждого диапазона мы храним счетчик, число внутренних активных позиций. Напомним, что <tex>B_{j}[\ell]=1</tex> только когда <tex>l</tex>-й счетчик не равен нулю. Чтобы эффективно обновить список <tex>(j-1)</tex>-активных позиций и получить список <tex>j</tex>-активных позиций, мы также храним для каждого <tex>j'</tex> список указателей на пары соседних позиций, таких, что одна из них должна быть удалена, когда мы достигнем <tex>j=j'</tex>. Всякий раз когда появляется новая пара соседних позиций <tex>p_{z},\ p_{z+1}</tex>, мы считаем <tex>L=</tex> <tex>lcp</tex> <tex>(T[p_{z} \ldots],\ T[p_{z+1} \ldots])</tex> и с этого момента наименьший <tex>j'=p_{z}+L</tex>, когда одна из них должна быть удалена из списка, и вставляем указатель на пару <tex>p_{z},\ p_{z+1}</tex> в <tex>j'</tex>-й список. Когда мы действительно достигнем <tex>j=j'</tex>, мы проследуем по указателю и проверим, что <tex>p_{\ell}</tex> и <tex>p_{\ell+1}</tex> по-прежнему являются соседями. Если это так, мы удаляем соответствующую позицию из списка активных позиций. Иначе мы ничего не делаем. |
Из леммы 13 следует, что два возможных обновления списка при переходе от <tex>j-1</tex> к <tex>j</tex> добавляют <tex>j</tex> или удаляют какую-то позицию из списка. Это гарантирует, что процесс удаления из леммы 13 и процесс, который мы описали, эквивалентны. | Из леммы 13 следует, что два возможных обновления списка при переходе от <tex>j-1</tex> к <tex>j</tex> добавляют <tex>j</tex> или удаляют какую-то позицию из списка. Это гарантирует, что процесс удаления из леммы 13 и процесс, который мы описали, эквивалентны. | ||
− | Предположим, что мы уже знаем список <tex>(j-1)</tex>-активных позиций, битовый вектор <tex>B_{j-1}</tex>, и число <tex>(j-1)</tex>-активных позиций в каждом диапазоне <tex>R_{j-1}^{\ell}</tex>. Сначала мы обновим список <tex>(j-1)</tex>-активных позиций. Когда позиция удалена из списка, мы находим диапазон, к которому она принадлежит и уменьшаем его счетчик внутренних позиций. Если счетчик становится нулевым, мы очищаем соответствующий бит битового вектора. Далее мы начинаем обновлять разбиение: сначала мы добавляем новый диапазон <tex>[j,\ j]</tex> к разбиению <tex>[1 | + | Предположим, что мы уже знаем список <tex>(j-1)</tex>-активных позиций, битовый вектор <tex>B_{j-1}</tex>, и число <tex>(j-1)</tex>-активных позиций в каждом диапазоне <tex>R_{j-1}^{\ell}</tex>. Сначала мы обновим список <tex>(j-1)</tex>-активных позиций. Когда позиция удалена из списка, мы находим диапазон, к которому она принадлежит и уменьшаем его счетчик внутренних позиций. Если счетчик становится нулевым, мы очищаем соответствующий бит битового вектора. Далее мы начинаем обновлять разбиение: сначала мы добавляем новый диапазон <tex>[j,\ j]</tex> к разбиению <tex>[1 \ldots j-1]</tex> и инициализируем счетчик активных позиций единицей. Затем, мы обновлям первые <tex>2k+4</tex> диапазонов (<tex>k</tex> {{---}} максимальная степень <tex>2</tex>, которой кратно <tex>j</tex>), используя теорему 15, а также счетчики и битовый вектор. Этот процесс займет <tex>\mathcal{O}(k)</tex> времени, что амортизированно составляет |
<tex>\displaystyle \mathcal{O}(\sum_{k=1}^{\infty}\frac{k}{2^{k}})=\mathcal{O}(1)</tex> при всех значениях <tex>j</tex>. | <tex>\displaystyle \mathcal{O}(\sum_{k=1}^{\infty}\frac{k}{2^{k}})=\mathcal{O}(1)</tex> при всех значениях <tex>j</tex>. | ||
Строка 383: | Строка 407: | ||
|statement= Строка <tex>T</tex> длины <tex>n</tex> может храниться в структуре данных памяти <tex>\mathcal{O}(n)</tex>, которая позволяет вычислять максимальный суффикс любой подстроки <tex>T</tex> за <tex>\mathcal{O}(1)</tex> времени. Данную структуру данных можно построить за <tex>\mathcal{O}(n)</tex> времени. | |statement= Строка <tex>T</tex> длины <tex>n</tex> может храниться в структуре данных памяти <tex>\mathcal{O}(n)</tex>, которая позволяет вычислять максимальный суффикс любой подстроки <tex>T</tex> за <tex>\mathcal{O}(1)</tex> времени. Данную структуру данных можно построить за <tex>\mathcal{O}(n)</tex> времени. | ||
}} | }} | ||
+ | |||
+ | ==См. также== | ||
+ | * [[Алгоритм Крочемора]] | ||
+ | * [[Суффиксный массив]] | ||
+ | * [[Алгоритм цифровой сортировки суффиксов циклической строки]] | ||
==Примечания== | ==Примечания== |
Текущая версия на 19:13, 4 сентября 2022
Декомпозиция Линдона была изобретена Роджером Линдоном (англ. Roger Lyndon) в 1954 году. Она используется для нахождения лексикографически минимального и максимального суффиксов строки, а также лексикографически минимального циклического сдвига.
Содержание
Основные определения
Определение: |
Простая строка — строка, которая лексикографически меньше любого своего собственного суффикса. |
Примеры:
— простая строка, так как , , , .
— не простая строка, так как .
Определение: |
Декомпозиция Линдона (англ. Lyndon decomposition) строки | — её разложение , где строки просты, и при этом .
Существование и единственность
Лемма: |
1. 2. — простая |
Доказательство: |
1. Так как , то либо является префиксом , тогда:
Следовательно любого суффикса (так как по условию явлеяется простой строкой), либо и , .Из обоих ситуаций следует, что 2. Пусть — суффикс строки . Тогда рассмотрим 3 возможных случая:
|
Теорема (Чен-Линдон-Фокс): |
Можно построить декомпозицию Линдона любой строки , причем единственным образом. |
Доказательство: |
1. Существование. У каждой строки существует хотя бы одно разбиение на простые слова. Это следует из того, что отдельный символ является простым словом. Тогда среди всех разбиений строки на простые слова возьмём то, в котором меньше всего слов. Покажем, что это и будет декомпозицией Линдона данной строки. Предположим, что это не так. Значит, леммы следует, что эти слова можно сконкатенировать и получить разбиение строки на меньшее число слов. Получили противоречие. . Так как слова и простые, то из доказаннойТаким образом доказали даже более сильное утверждение: , — минимально нет2. Единственность. Пусть существует несколько разбиений , удовлетворяющих условию теоремы. Сравним длины первых двух слов и , если , сравним вторые и так далее. Если длины всех слов одинаковы, то разбиения совпадают — противоречие. Иначе .Покажем, что такого не может быть: 1) Пусть , тогда , где — префикс , . Тогда получаем:
Пришли к противоречию: .2) Случай То есть не может быть строк симметричен разобранному. и несовпадающей длины, значит, разбиения равны. |
Алгоритм Дюваля
Алгоритм
Алгоритм Дюваля (англ. Duval's algorithm) находит для данной строки длины
декомпозицию Линдона за время с использованием дополнительной памяти. Он строит декомпозицию только на упорядоченных алфавитах.
Определение: |
Предпростая строка — строка | , такая что , где — некоторая простая строка, а — некоторый префикс строки .
Во время работы алгоритма строка представляется в виде конкатенации трёх строк , где для строки декомпозиция Линдона уже найдена, и уже больше не используется алгоритмом; строка — это предпростая строка; строка — ещё не обработанная алгоритмом часть строки . Алгоритм Дюваля берёт первый символ строки и пытается дописать его к строке . При этом, возможно, для какого-то префикса строки декомпозиция Линдона становится известной, и эта часть переходит к строке .
Будем поддерживать три указателя:
- — на начало строки
- — на текущий символ в строке , с которым будет производиться сравнение
- — на начало строки
Внешний цикл алгоритма будет выполняться, пока
, то есть пока вся строка не перейдёт в строку . Внутри этого цикла создаются два указателя и . Затем будем пытаться добавить символ к строке , для чего необходимо произвести сравнение с символом . При этом будем поддерживать инвариант: — длина подстроки .Возникают три различных случая:
- тогда дописывыем символ к строке и увеличиваем оба указателя на единицу.
- тогда строка станет простой. Значит, мы увеличим на единицу, а передвигаем обратно на , чтобы следующий символ сравнивался с первым символом . То есть получаем новую простую строку длины .
- значит, строка уже не может быть предпростой. Добавляем к все строки , а по нашему инварианту мы знаем, что их длина равна , затем сдвигаем к началу позиции строки . После чего внешний цикл запускаем заново:
Реализация
function lyndon(string s, string[] decomposition): ns.length i 0 cur 0 while i n j i k i + 1 while k n and s[j] s[k] if s[j] s[k] j i else j k + 1 k k + 1 while i j decomposition[cur] s[i..i + k - j - 1] cur cur + 1 i i + k - j
Корректность
Покажем, что алгоритм получает нужное разложение. То есть все
— простые, и лексикографически.При обработке текущего символа в первом случае просто сдвигаем указатели, не записывая ответ. Мы сравниваем символы в
и на одинаковых позициях, а — префикс , поэтому инвариант сохраняется.Во втором случае объединяем все найденные
с и получем новую строку .Покажем, что
является простой. Рассмотрим ее суффикс. Если он начинается в середине , сравним его посимвольно со строкой , и тогда в каком-то символе он окажется больше , так как суффикс начинается с — суффикса , а строка — простая и по определению меньше всех своих суффиксов. Если суффикс начинается в , то при сравнении расхождение будет в символах и . Но , так что суффикс больше . Если же суффикс начинается с первой позиции какой-то подстроки , то отбросим общий префикс вида и придем к предыдущему случаю.В третьем случае просто выведем все
и продолжим обработку со строки , так как при добавлении , перестанет удовлетворять требованиям, ведь в этом случае суффикс строки равный будет меньше .Теперь покажем, что
.Последоваельность из
будет удовлетворять условию, так как эти строки равны. Следующее слово будет иметь общий префикс с , а после него будет стоять символ, меньший следующего символа из (новое получается по третьему случаю), либо следующее слово будет просто префиксом , и, как следствие, оно будет меньше лексикографически.Асимптотика
Внешний цикл
делает не более итераций, поскольку в конце каждой его итерации увеличивается как минимум на . Второй внутренний цикл выполнится суммарно не более , так он добавляет к ответу все символы, причём каждый символ лишь единожды.Оценим теперь количество итераций первого вложенного цикла
. Для этого рассмотрим второй вложенный цикл — он при каждом своём запуске выводит некоторое количество копий одной и той же простой строки некоторой длины . Заметим, что строка является предпростой, причём её простые строки имеют длину как раз , то есть её длина не превосходит . Поскольку длина строки равна , а указатель увеличивается на единицу на каждой итерации первого вложенного цикла , то этот цикл выполнит не более итераций. Худшим случаем является случай , и мы получаем, что первый вложенный цикл всякий раз выполняет не менее итераций. Вспоминая, что всего выводится символов, получаем, что для вывода символов требуется не более итераций первого вложенного .Итого получаем, что итоговая асимптотика алгоритма составляет
.Отметим, что алгоритму требуется
памяти: на указатели .Поиск лексикографически минимального суффикса строки
Поиск лексикографически минимального и максимального суффиксов строки — вопрос, который часто поднимается при решении различных теоретических задач. С помощью классического алгоритма Дюваля эта задача решается за линейное время и константный размер дополнительной памяти.
Если заметить, что данная нам строка
является подстрокой заранее данного текста длиной , то выполнив некоторый предподсчёт, мы можем получать значения максимального и минимального суффиксов определённой подстроки гораздо быстрее, чем линейно. Это может быть очень полезным при работе с большими объёмами данных (такими как генетический код и т.д.)Покажем, что
существует структура данных, размер которой линейно зависит от длины данного текста, со временем запроса и временем препроцессинга для запросов на нахождение минимального суффикса.Для данных индексов суффиксный массив и инвертированный суффиксный массив строки соответственно. и могут быть улучшены за , чтобы отвечать на запросы вида
будем обозначать как массив - подмассив с индекса по массива всех суффиксов строки. Множество всех непустых суффиксов строки будем обозначать для краткости как . Также, будем обозначать и- по данным подстрокам наибольший общий префикс и определить, какая из подстрок лексикографически меньше и строки найти
- по индексам и вычислить максимальный и минимальный суффикс в
Более того, такой улучшенный суффиксный массив может отвечать на запрос "по данным
— подстрокам вычислить максимальное чило , такое, что является префиксом " за константное время. Действительно, стоит заметить, что если — префикс , тоЗапросы к перевёрнутому улучшенному суфмассиву
также имеют смысл. С его помощью мы можем для пары подстрок найти их наибольший общий суффикс и наибольшее число , такое, что является суффиксом .Возьмём строку
длины . Для каждой позиции мы выберем подстрок , которые мы назовём каноническими. Определим как -ю кратчайшую каноническую подстроку, заканчивающуюся в позиции . Для пары целых чисел мы определим как наибольшее , такое, что — суффикс .Мы потребуем, чтобы канонические подстроки удовлетворяли определённым условиям:
- и для некоторого выполняется
- и можно вычислить за константное время для данных и соответственно
Такая структура данных работает при любом выборе канонических подстрок, которые удовлетворяют вышеприведённым условиям, например при простейшем
Лемма (0): |
Пусть [1] - искомый лексикографически наименьший суффикс. Если у нет непустых бордеров, то . Иначе — кратчайший непустой бордер . |
Доказательство: |
Покажем, что является одновременно префиксом и суффиксом . Если , то лемма доказана, иначе . По определению лексикографического порядка, либо является префиксом , либо существует такой, что , и .В случае Следовательно, выполняется и таким образом также является суффиксом . Покажем, что второй случай никогда не выполняется. Действительно, , но в является лексикографически наименьшим суффиксом. является одновременно префиксом и суффиксом . Если не имеет непустых бордеров, то . Иначе, является бордером . Предположим, - не наименьший непустой бордер , тогда существует более короткий бордер . По определению, — префикс , следовательно он также является префиксом . Следовательно, , что приводит нас к противоречию. |
Лемма (1): |
Минимальный суффикс равен либо
Более того, , где — начальная позиция минимального суффикса в , либо минимальному суффиксу . может быть найдено за константное время с использованием улучшенного суффиксного массива строки . |
Доказательство: |
По лемме 0 минимальный суффикс равен либо | , либо его кратчайшему непустому бордеру. Более того, в последнем случае длина минимального суффикса равна не превышает . С другой стороны, по второму свойству канонических подстрок, длина равна как минимум . Таким образом, во втором случае минимальный суффикс является минимальным суффиксом . Заметим, что для значения не определены, но тогда выполняется случай из условия леммы. Чтобы доказать финальное выражение, вспомним, что нахождение минимального суффикса — одна из базовых операций, поддерживаемых улучшенным суфмассивом.
Требуемая структура данных, помимо улучшенного суфмассива, должна, для каждого
содержать битовый вектор длиной . Положим тогда и только тогда, когда минимальный суффикс длиннее, чем . Для мы всегда считаем , поскольку является минимальным суффиксом самого себя. Вспомним, что количество канонических подстрок для каждого равна , поэтому каждый вмещается в константное количество машинных слов и структура данных занимает памяти.Алгоритм запроса
Предположим, что мы ищем минимальный суффикс
c . Наш подход основан на лемме 1. Если выполняется случай , лемма позволяет нам вычислить ответ за . В общем случае, мы найдём минимальный суффикс , сравним его с и вернём меньший из них.Мы используем лемму 1 и битовый вектор
чтобы посчитать минимальный суффикс . Назовём наибольший индекс, не превышающий , такой, что . Заметим, что такой индекс всегда существует (поскольку ) и может быть найден за константное время с использованием битовых операций. Для любого индекса мы имеем , т.е., случай леммы 1 выполняется для . Тогда, по индукции, минимальный суффикс на самом деле является минимальным суффиксом . С другой стороны, , поэтому для последнего мы можем гарантировать, что выполняется первый случай леммы, что позволяет нам найти минимальный суффикс за константное время.Построение искомой структуры данных
Простой алгоритм построения с временем работы
также основывается на лемме 1. Покажем, что построив улучшенный суфмассив, мы можем найти за . Мы ищем минимальный суффикс для последовательных значений . Как только мы получили результат , случай леммы 1 даёт нам второго кандидата на минимальный суффикс , и наш улучшенный суфмассив позволяет нам выбрать наименьшего из этих двух кандидатов. Мы устанавливаем если меньший кандидат не содержится в . Стало быть, мы получили следующий результат:Теорема (2): |
Строку длины можно уместить в структуру данных с памяти, которая позволяет вычислять минимальный суффикс любой подстроки за . Эта структура данных может быть построена за . |
Вышеописанная конструкция проста и работает для любого выбора канонических подстрок, но, к сожалению, она не может быть использована для достижения компромисса между временем запроса и временем построения. Далее мы предложим особый способ выбора канонических подстрок и опишем альтернативный метод построения. Этот способ основывается на предположении, что по данной строке длины
мы можем найти минимальный суффикс для всех её префиксов за . Следовательно, нам удобно иметь много , которые являются префиксами друг друга. Тогда, естественным будет выбрать , поскольку все подстроки являются префиксами . К сожалению, подстроки, выбранные таким способом, не удовлетворяют условию , и, посему, нам необходимо немного изменить его.Для
мы определим . Для установим и определим таким образом:Заметим, что если
, то , в то время как, если , то . Очевидно, что количество таких подстрок, заканчивающихся в получается . Докажем далее, что канонические подстроки, выбранные вышеуказанным способом, имеют необходимые свойства.Утверждение (3): |
Для любого и при мы имеем |
Для неравенство, очевидно, выполняется. Рассмотрим . Обозначим через , как и ранее, . Если чётно, то нечётно и мы имеем , в то время как, для нечётного выполняется |
Утверждение (4): |
Для , величина может быть посчитана за константное время. |
Положим . Заметим, что
Таким образом, . , и мы можем за константное время проверить, какое из этих трёх значений корректно. |
После построения улучшенного суфмассива, мы установили все биты[2]. Разделим на куски длиной (где ) и запустим этот алгоритм для каждых четырёх (или менее, в конце) последовательных кусков. Это даст нам минимальные суффиксы для всех , за время . Значение определено с помощью сравнения длины вычисленного минимального суффикса с . У нас фаз алгоритма, что даёт нам время и требуемой памяти.
в 1. После этого, для каждого мы посчитали минимальные суффиксы подстрок , как указано далее. Зафиксируем и разобьём на куски размером (где ) . Теперь каждый является префиксом конкатенации максимум 4х таких кусков. Известно, что по данной строке можно посчитать длины минимальных суффиксов всех её префиксов за линейное время с помощью одной из вариаций алгоритма ДюваляКомпромисс
Чтобы получить структуру данных, со временем построения
и временем запроса , мы немного по-другому определим битовые вектора. Положим размером , притом тогда и только тогда, когда или минимальный суффикс длиннее, чем . В этом случае, нам необходимо только фаз в алгоритме построения, поэтому он займёт времени.Как и ранее, предположим, что мы ищем минимальный суффикс
, при . Самое сложное в этом — найти минимальный суффикс , и далее необходимо найти , такой, что минимальный суффикс на самом деле является минимальным суффиксом , но длиннее, чем . Если для целого , мы можем найти наибольший , такой, что , и нам будет известно, что . В общем случае, мы выберем наибольший , такой что , и будем знать, что мы должны рассмотреть и , где определён, как в предыдущем случае. В результате, мы имеем возможных значений , и нам известно, что искомый суффикс может быть найден, используя случай леммы 1 для для каждого из этих значений. Тогда мы просто сгенерируем всех этих кандидатов и используем улучшенный суфмассив, чтобы найти наименьший суффикс среди них. В результате, запрос к нашей структуре данных будет выполняться за .
Из вышеописанного следует теорема:
Теорема (5): |
Для любого , строка длиной может храниться в структуре данных, занимающей памяти, позволяющей вычислять минимальный суффикс любой из подстрок за время . Такая структура данных может быть построена за . |
Поиск лексикографически максимального суффикса строки
Наша структура данных, необходимая для поиска максимального суффикса, очень похожа на ту, что мы разработали для минимального суффикса. Однако, в отличие от той проблемы, свойства максимальных суффиксов позволят нам добиться линейной асимптотики.
Заметим, что единственный компонент из части о минимальном суффиксе, который не может быть сразу адаптирован к задаче о максимальном суффиксе, это лемма 1. Так как эта лемма неприменима к нашей задаче, далее мы докажем следующую лемму, эквивалентную в смысле алгоритмического приложения. Канонические подстроки
обозначены как и ранее.Лемма (7): |
Рассмотрим подстроку . Используя улучшенный суффиксный массив строки , за времени можно найти такой индекс , что максимальный суффикс строки равен либо
, либо максимальному суффиксу строки |
Доказательство: |
Доказательство приводится ниже, с использованием вспомогательных лемм. |
Точно так же, как и структура, описанная в части о минимальном суффиксе, наша структура данных, не считая улучшенный суффиксный массив, содержит битовые вектора здесь, очевидно, может быть адаптирован к нашей задаче, только вместо леммы 1 мы будем использовать лемму 7 и выбирать наибольшего из двух кандидатов в качестве ответа. Это демонстрирует следующая теорема:
, с , если или максимальный суффикс строки длиннее . Алгоритм запроса, описанный
Теорема (8): |
Строка длины может храниться в структуре данных с памяти, которая позволяет вычислять максимальный суффикс любой подстроки строки за время . |
Доказательство: |
Алгоритмы построения за здесь и здесь соответственно, также легко адаптируются к нашей задаче. В случае поиска максимального суффикса, тем не менее, мы можем добиться времени построения , как будет показано ниже. | и компромисс между временем запросов и временем построения, описанные
Доказательство основной леммы
Ниже мы описываем алгоритм, работающий за константное время, который возвращает позицию
.Заметим, что если максимальный суффикс
of короче, чем (случай (b) леммы 7), алгоритм может вернуть любое . Далее мы предполагаем, что длиннее, чем и показываем, что при этом предположении алгоритм вернёт . Из нашего предположения свойств канонических подстрок следует, что , where , и что длины суффиксов подстроки , начинающихся с позиций в промежутке , отличаются не более чем в два раза.Мы начнем со вспомогательной леммы, которая обозначалась как лемма 2 в [1]
Лемма (9): |
Пусть — префикс строки и пусть , где — максимальный суффикс в . Если не является префиксом , тогда . Иначе, также является префиксом строки . |
Доказательство: |
Пусть [1], но здесь мы приводим доказательства вследствие другого обозначения. | — максимальный суффикс в и — максимальный суффикс в . Очевидно, является префиксом строки . Предположим, что — префикс (иначе по лемме 9). Длины и различаются не более чем в два раза, поэтому . Благодаря этому, и имеют некоторые интересные свойства, описанные в последующих леммах. Эти леммы по существу повторяют леммы 4 и 5 из
Лемма (10): |
Подстрока — минимальный период строки , т.е. — кратчайшая строка, такая, что для какого-то выполняется . |
Доказательство: |
Поскольку Предположим, что — бордер — период . Остается лишь доказать, что невозможно найти более короткий период. Таким образом, рассмотрим(consider) кратчайший период , и предположим, что . Тогда , и по лемме о периодичности подстрока имеет другой период . Так как — кратчайший период, то должно быть степенью , т.е. для какого-то . (лексикографически меньше). Тогда приписывание к обеим частям последнего неравенства степеней даёт для любого , таким образом из транзитивности получаем , что противоречит максимальности in . Таким образом, , и следовательно . Но и , поэтому больше, чем и принадлежит , противоречие. Заметим, что на самом деле , потому что , поэтому |
Лемма (11): |
Положим, . Тогда максимальный суффикс — длиннейший суффикс строки равен для некоторого . (См. рисунок 1) |
Доказательство: |
Очевидно, что [3]. является бордером . Из и имеем . Следовательно вхождения в качестве префикса и в качестве суффикса строки перекрывают друг друга как минимум в позициях. Т.к. — период , отсюда следует, что также является периодом . Таким образом, , где — целое число и — суффикс . Более того, — это префикс , поскольку является префиксом , который в свою очередь является префиксом . Теперь будет означать нетривиальное вхождение в , которое противоречит примитивности , смотриРис. 1: Схематичная иллюстрация к лемме 11.
|
Доказательство леммы 7
Пусть
— максимальный суффикс в и — максимальный суффикс в . Сначала вычислим и за , используя улучшенный суффиксный массив. Затем проверим, что является префиксом . Если это неверно, то мы возвращаем . Иначе, мы вычисляем максимальное целое число , такое, что (для ) — суффикс , используя метод из алгоритма поиска минимального суффикса, и возвращаем . Из вышеописанных лемм следует, что если длиннее , тогда .
Построение структуры данных
Наш алгоритм основывается на следующем понятии. Для
мы говорим, что позиция -активна, если не существует такой позиции , что . Другими словами, — -активна тогда и только тогда, когда — максимальный суффикс самого себя. Максимальный суффикс любой строки является своим собственным максимальным суффиксом, поэтому из определения следует, что стартовая позиция максимального суффикса строки — это минимальная -активная позиция в промежутке . Следовательно, при мы имеем тогда и только тогда, когда существует как минимум одна -активная позиция в диапазоне . Положим , тогда это равенство также сохраняется для (поскольку всегда -активна)Пример (12): Если
, то 8-активными позициями будутАлгоритм построения перебирает здесь, которые образуют разбиение . Два следующих результата описывают изменения списка -активных позиций и диапазонов , когда мы увеличиваем .
от до , сохраняя список активных позиций и вычисляя битовые вектора . Мы также сохраняем диапазоны для выбора канонических подстрок, описанныхЛемма (13): |
Если список всех -активных позиций состоит из . . . , то список -активных позиций может быть создан путём добавления , и повторения следующей процедуры: если и — два соседа в текущем списке и , удаляем или из списка, зависимо от того, что или , соответственно. |
Доказательство: |
Сначала мы докажем, что если позиция Далее, заметим, что если не является -активной, то она также не является и -активной. Действительно, если не -активна, тогда по определению существует позиция , такая, что . Следовательно, и не является -активной. Отсюда, единственными кандидатами на -активные позиции остаются -активные позиции и . — -активная позиция и — префикс , то тоже является -активной. Если это не так, тогда существует позиция , такая, что , и из этого следует, что , противоречие. -активная позиция не является -активной только если (1) или (2) существует такая, что — префикс , т.е. — -активна и или, другими словами, . Оба этих случая выявляются процедурой удаления. |
Пример (14): Если
, то 8-активными позициями будут: и 9-активными позициями будут: т.е. мы добавляем в наш список 8-активных позиций, а затем удаляем .Утверждение (15): |
Пусть и предположим, что — максимальная степень двойки, которой кратно .
Если , то . Если , то . Если , то . Если , то . |
Заметим, что у нас есть и , в то время как для
где . Также заметим, что
Более того, и , что упрощает проверку заявленных формул. Заметим, что возможно, что определено только для значений , меньших, чем . Это именно тот случай, когда число диапазонов растёт на единицу, иначе оно остается неизменным. |
Рис. 2 Разбиения
на при и . При , и и объединяются в . На самом деле, все длины являются степенями двойки, но наш алгоритм не использует это наблюдение.
Мы просматриваем позиции строки слева направо, вычисляя битовые вектора. Мы сохраняем список активных позиций и разбиение на диапазоны . Кроме того, для каждого диапазона мы храним счетчик, число внутренних активных позиций. Напомним, что только когда -й счетчик не равен нулю. Чтобы эффективно обновить список -активных позиций и получить список -активных позиций, мы также храним для каждого список указателей на пары соседних позиций, таких, что одна из них должна быть удалена, когда мы достигнем . Всякий раз когда появляется новая пара соседних позиций , мы считаем и с этого момента наименьший , когда одна из них должна быть удалена из списка, и вставляем указатель на пару в -й список. Когда мы действительно достигнем , мы проследуем по указателю и проверим, что и по-прежнему являются соседями. Если это так, мы удаляем соответствующую позицию из списка активных позиций. Иначе мы ничего не делаем.
Из леммы 13 следует, что два возможных обновления списка при переходе от к добавляют или удаляют какую-то позицию из списка. Это гарантирует, что процесс удаления из леммы 13 и процесс, который мы описали, эквивалентны.
Предположим, что мы уже знаем список
-активных позиций, битовый вектор , и число -активных позиций в каждом диапазоне . Сначала мы обновим список -активных позиций. Когда позиция удалена из списка, мы находим диапазон, к которому она принадлежит и уменьшаем его счетчик внутренних позиций. Если счетчик становится нулевым, мы очищаем соответствующий бит битового вектора. Далее мы начинаем обновлять разбиение: сначала мы добавляем новый диапазон к разбиению и инициализируем счетчик активных позиций единицей. Затем, мы обновлям первые диапазонов ( — максимальная степень , которой кратно ), используя теорему 15, а также счетчики и битовый вектор. Этот процесс займет времени, что амортизированно составляет при всех значениях .Из вышеописанного следует теорема:
Теорема (16): |
Строка длины может храниться в структуре данных памяти , которая позволяет вычислять максимальный суффикс любой подстроки за времени. Данную структуру данных можно построить за времени. |