Алгоритм Карккайнена-Сандерса — различия между версиями
(→Шаг 2) |
(→Алгоритм skew) |
||
Строка 76: | Строка 76: | ||
Получили асимптотическое уравнение <tex> T(n) = T(\frac23 n) + O(n) </tex>, решением которого также является <tex> T(n) = O(n) </tex> (это видно из того, что сумма геометрической прогрессии с основанием <tex> \frac23 </tex> равна <tex> 3n </tex>). | Получили асимптотическое уравнение <tex> T(n) = T(\frac23 n) + O(n) </tex>, решением которого также является <tex> T(n) = O(n) </tex> (это видно из того, что сумма геометрической прогрессии с основанием <tex> \frac23 </tex> равна <tex> 3n </tex>). | ||
− | {{ | + | === Шаг 1 === |
+ | На этом шаге строится суффиксные массивы <tex> A_{S_1} </tex> и <tex> A{S_2} </tex> для множества суффиксов <tex> \{ S[i..n-1] | i \mod 3 \ne 0 \} </tex>. | ||
+ | # Получим строку <tex> S' </tex> аналогично предыдущему алгоритму: | ||
+ | #* Сделаем список, состоящий из троек <tex> S[i..i+2]; i \mod 3 \ne 0 </tex>, причем примем <tex> S[n-2..n] = S[n-2..n-1]\$ </tex>, а <tex> S[n-1..n+1] = S[n-1]\$\$ </tex>. | ||
+ | #* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит <tex> \Sigma' </tex>. | ||
+ | #* Перекодируем строку <tex> S </tex> в строку <tex> S' </tex> в алфавите <tex> \Sigma' </tex> следущим образом: <tex> S' = [ \Sigma'(s[i..i+2]) | i \mod 3 == 1 ] + [ \Sigma'(s[i..i+2]) | i \mod 3 == 2 ] </tex>. Как можно заметить, длиной строки <tex> S' </tex> будет <tex> n' = \frac23 n </tex>. Суффиксу <tex> S[i..n-1] </tex> в старом алфавите, где <tex> i \mod 3 == 1 </tex>, в новом алфавите будет соответствовать строка <tex> S'[\frac{i-1}{3}..\frac{n}{3} - 1] </tex>, а если <tex> i \mod 3 == 2 </tex>, то строка <tex> S'[\frac{n}{3} + \frac{i-2}{3}..\frac{2n}{3} - 1] </tex>. | ||
+ | # Вызовем алгоритм рекурсивно для строки <tex> S' </tex>, получив суффиксный массив <tex> A_{S'} </tex>. | ||
+ | # Пройдем по массиву <tex> A_{S'} </tex>. Если <tex> A_{S'}[i] < \frac{n}{3} </tex>, то этот суффикс соответствует позиции <tex> j </tex>, где <tex> j \mod 3 == 1 </tex>, тогда добавим в массив <tex> A_{S_1} </tex> значение <tex> 3A_{S'}[i] + 1 </tex>. Если же <tex> A_{S'}[i] \ge \frac{n}{3} </tex>, то этот суффикс соответствует позиции <tex> j </tex>, где <tex> j \mod 3 == 2 </tex>, тогда добавим в массив <tex> A_{S_2} </tex> значение <tex> 3(A_{S'}[i] - \frac{n}{3}) + 2 </tex>. Псевдокод этого шага: | ||
+ | <tex> A_{S_1} </tex> = [] | ||
+ | <tex> A_{S_2} </tex> = [] | ||
+ | for i = 0..<tex>A_{S'}</tex>.length: | ||
+ | if <tex>A_{S'}</tex>[i] < n / 3: | ||
+ | <tex>A_{S_1}</tex>.add(3 * <tex>A_{S'}</tex>[i] + 1) | ||
+ | else: | ||
+ | <tex>A_{S_2}</tex>.add(3 * (<tex>A_{S'}</tex>[i] - n / 3) + 2) | ||
+ | === Шаг 2 === | ||
+ | ололо | ||
== Ссылки == | == Ссылки == |
Версия 18:48, 29 марта 2012
Алгоритм Каркайнена-Сандерса (Karkkainen, Sanders) — алгоритм построения суффиксного массива за линейное время.
Определение: |
Четным суффиксом назовем суффикс, начинающийся в четной позиции. Нечетным суффиксом — суффикс, начинающийся в нечетной позиции. |
Содержание
Базовая идея
Алгоритм базируется на алгоритме Фараха[1] построения суффиксного дерева за линейное время:
- Строим суффиксное дерево для четных суффиксов рекурсивно сведя задачу к построению суффиксного дерева для строки половинной длины.
- Строим суффиксное дерево для нечетных суффиксов за линейное время, используя результат для четных позиций.
- Сливаем суффиксные деревья за линейное время.
Получили асимптотическое уравнение
, решением которого является .Алгоритм
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением $ в конец). На шаге сливания мы сможем избавиться от него.
Шаг 1
На первом шаге мы строим суффиксный массив
для суффиксов строки , начинающихся в четных позициях.- Отобразим исходную строку
- Сделаем список, состоящий из пар символов вида , где .
- Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит .
- Перекодируем строку в алфавит , получив строку половинной длины.
длины в строку длины следующим образом:
- Рекурсивно построим суффиксный массив .
- Построим суффиксный массив . Очевидно, , так отношение упорядоченности любых двух строк в старом алфавите эквивалентно отношению упорядоченности в новом алфавите по его построению.
Шаг 2
На этом шаге мы за линейное время получим суффиксный массив
для нечетных суффиксов, используя уже построенный .Заметим, что сортировка множества нечетных суффиксов
аналогична сортировке множества пар . Однако — четный суффикс, и его относительную позицию мы уже узнали на шаге 1.Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив
), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. Так была потребована четность длины строки, последним суффиксом будет нечетный, ему будет соответствовать пара . Псевдокод этого шага:M = [] M.add(Pair(S[n-1], n)) for i = 0..n/2 - 1: if== 0: //перед первым положительным суффиксом ничего не может стоять, поэтому пропускаем его continue else: M.add(Pair(S[ -1], ))
Заметим, что массив
явно не отсортирован по вторым элементам и хранит не суффиксы, а их позиции в строке , но главное — что он отсортирован по возрастанию соответствующих этим позициям четным суффиксам. После устойчивой сортировки массива подсчетом по первому элементу легко восстановить массив := [] for i = 0..n/2 - 1: .add(M[i].second - 1)
Получили, что весь второй шаг требует времени.
Шаг 3
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам. В случае суффиксного массива слияние становится очень сложным [2]. Однако простой модификацией алгоритма можно значительно упростить его.
Пример
Покажем первые два шага агоритма для строки abaaab.
Во-первых, добавим защитный символ $, получив строку abaaab$. Во-вторых, дополним ее до четной длины, получив abaaab$$.
Шаг 1
- В новом алфавите будет три элемента — ab, aa, $$. Они получат номера 2, 1 и 0 соответственно.
- Сжатой строкой будет 2120.
- После рекурсивного вызова получим, что = [3, 1, 2, 0], и = [6, 2, 4, 0].
Шаг 2
- Обойдя массив , получим M = [(b, 6), (b, 2), (a, 4), ($, 8)].
- После сортировки подсчетом по первому элементу, получим M = [($, 8), (a, 4), (b, 6), (b, 2)].
- Восстановив массив , получаем [7, 3, 5, 1], что действительно является суффиксным массивом для нечетных суффиксов.
Алгоритм skew
Изменим изначальный алгоритм следующим образом:
- Построим суффиксный массив для суффиксов, соответствующих не кратным трем позициям. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.
- Построим суффиксный массив для суффиксов, соответствующих кратных трем позициям, используя результат первого шага за линейное время.
- Сливаем эти суффиксные массивы в один за линейное время.
Получили асимптотическое уравнение
, решением которого также является (это видно из того, что сумма геометрической прогрессии с основанием равна ).Шаг 1
На этом шаге строится суффиксные массивы
и для множества суффиксов .- Получим строку
- Сделаем список, состоящий из троек , причем примем , а .
- Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит .
- Перекодируем строку в строку в алфавите следущим образом: . Как можно заметить, длиной строки будет . Суффиксу в старом алфавите, где , в новом алфавите будет соответствовать строка , а если , то строка .
аналогично предыдущему алгоритму:
- Вызовем алгоритм рекурсивно для строки , получив суффиксный массив .
- Пройдем по массиву . Если , то этот суффикс соответствует позиции , где , тогда добавим в массив значение . Если же , то этот суффикс соответствует позиции , где , тогда добавим в массив значение . Псевдокод этого шага:
= [] = [] for i = 0.. .length: if [i] < n / 3: .add(3 * [i] + 1) else: .add(3 * ( [i] - n / 3) + 2)
Шаг 2
ололо
Ссылки
- ↑ M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf
- ↑ D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/