Алгоритм цифровой сортировки суффиксов циклической строки — различия между версиями
AKhimulya (обсуждение | вклад) м (→Пример) |
AKhimulya (обсуждение | вклад) (langle & rangle вместо less & greater) |
||
Строка 14: | Строка 14: | ||
На <tex>k</tex>-ой итерации имеем массивы <tex>p</tex> и <tex>c</tex>, вычисленные на предыдущей итерации. Приведем алгоритм, выполняющий <tex>k</tex>-ую итерацию за <tex>O(n)</tex>. Поскольку итераций всего <tex>O(\log n)</tex>, такой алгоритм имеет асимптотику <tex>O(n \log n)</tex>. | На <tex>k</tex>-ой итерации имеем массивы <tex>p</tex> и <tex>c</tex>, вычисленные на предыдущей итерации. Приведем алгоритм, выполняющий <tex>k</tex>-ую итерацию за <tex>O(n)</tex>. Поскольку итераций всего <tex>O(\log n)</tex>, такой алгоритм имеет асимптотику <tex>O(n \log n)</tex>. | ||
− | Заметим, что циклическая подстрока длины <tex>2^k</tex> состоит из двух подстрок длины <tex>2^{k-1}</tex>, которые мы можем сравнивать между собой за <tex>O(1)</tex>, используя информацию с предыдущей итерации — номера классов эквивалентности <tex>c</tex>. Таким образом, для подстроки длины <tex>2^k</tex>, начинающейся в позиции <tex>i</tex>, вся необходимая информация содержится в паре чисел <tex>\ | + | Заметим, что циклическая подстрока длины <tex>2^k</tex> состоит из двух подстрок длины <tex>2^{k-1}</tex>, которые мы можем сравнивать между собой за <tex>O(1)</tex>, используя информацию с предыдущей итерации — номера классов эквивалентности <tex>c</tex>. Таким образом, для подстроки длины <tex>2^k</tex>, начинающейся в позиции <tex>i</tex>, вся необходимая информация содержится в паре чисел <tex>\langle c[i], c[i + 2^{k-1}]\rangle </tex>. |
Отсортируем подстроки длины <tex>2^k</tex> по данным парам и запишем порядок в массив <tex>p</tex>. Воспользуемся здесь приёмом, на котором основана [[Цифровая сортировка| цифровая сортировка]]: отсортируем пары сначала по вторым элементам, а затем по первым (устойчивой сортировкой). Однако вторые элементы уже упорядочены — этот порядок задан в массиве от предыдущей итерации. Тогда, чтобы получить порядок пар по вторым элементам, надо от каждого элемента массива <tex>p</tex> отнять <tex>2^{k-1}</tex> (<tex>p</tex> даёт упорядочение подстрок длины <tex>2^{k-1}</tex>, и при переходе к строке вдвое большей длины эти подстроки становятся их вторыми половинками, поэтому от позиции второй половинки отнимается длина первой половинки). | Отсортируем подстроки длины <tex>2^k</tex> по данным парам и запишем порядок в массив <tex>p</tex>. Воспользуемся здесь приёмом, на котором основана [[Цифровая сортировка| цифровая сортировка]]: отсортируем пары сначала по вторым элементам, а затем по первым (устойчивой сортировкой). Однако вторые элементы уже упорядочены — этот порядок задан в массиве от предыдущей итерации. Тогда, чтобы получить порядок пар по вторым элементам, надо от каждого элемента массива <tex>p</tex> отнять <tex>2^{k-1}</tex> (<tex>p</tex> даёт упорядочение подстрок длины <tex>2^{k-1}</tex>, и при переходе к строке вдвое большей длины эти подстроки становятся их вторыми половинками, поэтому от позиции второй половинки отнимается длина первой половинки). | ||
Строка 30: | Строка 30: | ||
\multicolumn{3}{|l|}{0 iteration} & \multicolumn{4}{l|}{1 iteration} & \multicolumn{4}{l|}{2 iteration} & \multicolumn{4}{l|}{3 iteration} \\ \hline | \multicolumn{3}{|l|}{0 iteration} & \multicolumn{4}{l|}{1 iteration} & \multicolumn{4}{l|}{2 iteration} & \multicolumn{4}{l|}{3 iteration} \\ \hline | ||
p & & c & p & & & c & p & & & c & p & & & c \\ \hline | p & & c & p & & & c & p & & & c & p & & & c \\ \hline | ||
− | 7 & \$ & 1 & 7 & \$a & \ | + | 7 & \$ & 1 & 7 & \$a & $\langle1, 2\rangle$ & 1 & 7 & \$aba & $\langle1, 5\rangle$ & 1 & 7 & \$abacaba & $\langle1, 8\rangle$ & 1 \\ \hline |
− | 0 & a & 2 & 6 & a\$ & \ | + | 0 & a & 2 & 6 & a\$ & $\langle2, 1\rangle$ & 2 & 6 & a\$ab & $\langle2, 3\rangle$ & 2 & 6 & a\$abacab & $\langle2, 5\rangle$ & 2 \\ \hline |
− | 2 & a & 2 & 0 & ab & \ | + | 2 & a & 2 & 0 & ab & $\langle2, 3\rangle$ & 3 & 4 & aba\$ & $\langle3, 2\rangle$ & 3 & 4 & aba\$abac & $\langle3, 4\rangle$ & 3 \\ \hline |
− | 4 & a & 2 & 4 & ab & \ | + | 4 & a & 2 & 4 & ab & $\langle2, 3\rangle$ & 3 & 0 & abac & $\langle3, 4\rangle$ & 4 & 0 & abacaba\$ & $\langle4, 3\rangle$ & 4 \\ \hline |
− | 6 & a & 2 & 2 & ac & \ | + | 6 & a & 2 & 2 & ac & $\langle2, 4\rangle$ & 4 & 2 & acab & $\langle4, 3\rangle$ & 5 & 2 & acaba\$ab & $\langle5, 2\rangle$ & 5 \\ \hline |
− | 1 & b & 3 & 1 & ba & \ | + | 1 & b & 3 & 1 & ba & $\langle3, 1\rangle$ & 5 & 5 & ba\$a & $\langle5, 1\rangle$ & 6 & 5 & ba\$abaca & $\langle6, 7\rangle$ & 6 \\ \hline |
− | 5 & b & 3 & 5 & ba & \ | + | 5 & b & 3 & 5 & ba & $\langle3, 1\rangle$ & 5 & 1 & baca & $\langle5, 6\rangle$ & 7 & 1 & bacaba\$a & $\langle7, 6\rangle$ & 7 \\ \hline |
− | 3 & c & 4 & 3 & ca & \ | + | 3 & c & 4 & 3 & ca & $\langle4, 1\rangle$ & 6 & 3 & caba & $\langle6, 5\rangle$ & 8 & 3 & caba\$aba & $\langle8, 1\rangle$ & 8 \\ \hline |
\end{tabular} | \end{tabular} | ||
</tex> | </tex> | ||
Строка 108: | Строка 108: | ||
* [[Построение суффиксного массива с помощью стандартных методов сортировки]] | * [[Построение суффиксного массива с помощью стандартных методов сортировки]] | ||
− | == Источники == | + | == Источники информации == |
* [http://e-maxx.ru/algo/suffix_array MAXimal :: algo :: Суффиксный массив] | * [http://e-maxx.ru/algo/suffix_array MAXimal :: algo :: Суффиксный массив] | ||
Версия 12:29, 31 мая 2015
Содержание
Постановка задачи
Дана циклическая строка
. Суффиксом циклической строки называется строка (будем называть такую строкую суффиксом под номером i). Требуется отсортировать все её суффиксы в порядке лексикографической сортировки.Решение
Рассматриваемый алгоритм состоит из
итераций. На -той итерации ( ) сортируются циклические подстроки длины . На последней, -ой итерации, будут сортироваться подстроки длины , что эквивалентно сортировке циклических сдвигов.На каждой итерации алгоритм помимо перестановки
индексов циклических подстрок будет поддерживать для каждой циклической подстроки длиной , начинающейся в позиции , номер класса эквивалентности , которому эта подстрока принадлежит. В самом деле, среди подстрок могут быть одинаковые, и алгоритму понадобится информация об этом. Кроме того, номера классов эквивалентности будем давать таким образом, чтобы они сохраняли и информацию о порядке: если один суффикс меньше другого, то и номер класса он должен получить меньший.Описание алгоритма
На нулевой итерации отсортируем циклические подстроки длины сортировки подсчетом построим массив , содержащий номера суффиксов, отсортированных в лексикографическом порядке. По этому массиву построим массив классов эквивалентности .
, т.е. первые символы строк, и разделим их на классы эквивалентности (одинаковые символы должны быть отнесены к одному классу эквивалентности). При помощиНа
-ой итерации имеем массивы и , вычисленные на предыдущей итерации. Приведем алгоритм, выполняющий -ую итерацию за . Поскольку итераций всего , такой алгоритм имеет асимптотику .Заметим, что циклическая подстрока длины
состоит из двух подстрок длины , которые мы можем сравнивать между собой за , используя информацию с предыдущей итерации — номера классов эквивалентности . Таким образом, для подстроки длины , начинающейся в позиции , вся необходимая информация содержится в паре чисел .Отсортируем подстроки длины цифровая сортировка: отсортируем пары сначала по вторым элементам, а затем по первым (устойчивой сортировкой). Однако вторые элементы уже упорядочены — этот порядок задан в массиве от предыдущей итерации. Тогда, чтобы получить порядок пар по вторым элементам, надо от каждого элемента массива отнять ( даёт упорядочение подстрок длины , и при переходе к строке вдвое большей длины эти подстроки становятся их вторыми половинками, поэтому от позиции второй половинки отнимается длина первой половинки).
по данным парам и запишем порядок в массив . Воспользуемся здесь приёмом, на котором основанаЧтобы произвести устойчивую сортировку по первым элементам пар, воспользуемся сортировкой подсчетом, имеющую асимптотику
.Осталось пересчитать номера классов эквивалентности
, пройдя по новой перестановке и сравнивая соседние элементы (как пары двух чисел).Пример
s = abacaba$
Псевдокод
/* преобразует масив count, так что теперь он содержит позиции в массиве suffs с которых необходимо вставлять подстроки, начинающиеся с соответствующих символов */ calc_positions(count) count[0] = 0 for i = 2 .. count.length count[i] += count[i - 1] /* принимает строку, для которой требуется построить суффиксный массив возвращает суффиксный массив */ suff_array(str) s += '$' ALPHABET = 255 // нулевая итерация count = int[max(ALPHABET, str.length)] fill(count, 0) for ch in str count[ch]++ calc_positions(count) // suffs будет хранить индексы начал отсортированных подстрок текущей длины suffs = int[str.length] for ch in str suffs[count[ch]++] = i; classes = int[str.length] classesN = 0 last_char = '$' for suf in suffs if s[suf] != last_char last_char = s[suf[i]] classesN++ classes[suf] = classesN; // нулевая итерация завершена // сортируем подстроки длиной 2 * cur_len = 2^k curr_len = 1 while cur_len <= str.length // сортируем по второй половине подстроки sorted_by2 = int[str.length] for i = 0 .. str.length sorted_by2[i] = (suffs[i] + str.length - cur_len) % str.length // сортируем по первой половине // сортировка устойчивая, значит получим целиком отсортированные подстроки fill(count, 0) for by2 in sorted_by2 count[classes[by2]]++ calc_positions(count) for i = 0 .. str.length suffs[count[classes[sorted_by2[i]]]++] = sorted_by2[i] new_classes = int[str.length] classesN = 0 for i = 0 .. str.length mid1 = (suffs[i] + cur_len) % str.length mid2 = (suffs[i - 1] + cur_len) % str.length if classes[suffs[i]] != classes[suffs[i-1]] or classes[mid1] != classes[mid2] classesN new_classes[suffs[i]] = classesN classes = new_classes cur_len *= 2 return suffs