Алгоритм цифровой сортировки суффиксов циклической строки — различия между версиями
AKhimulya (обсуждение | вклад) (langle & rangle вместо less & greater) |
AKhimulya (обсуждение | вклад) (→Псевдокод) |
||
Строка 42: | Строка 42: | ||
==Псевдокод== | ==Псевдокод== | ||
− | /* преобразует масив count, так что | + | <font color=darkgreen>/* преобразует масив count, так что |
теперь он содержит позиции в массиве suffs с которых | теперь он содержит позиции в массиве suffs с которых | ||
− | необходимо вставлять подстроки, начинающиеся с соответствующих символов */ | + | необходимо вставлять подстроки, начинающиеся с соответствующих символов */</font> |
− | ''' | + | '''int[]''' calc_positions(count) |
count[0] = 0 | count[0] = 0 | ||
'''for''' i = 2 .. count.length | '''for''' i = 2 .. count.length | ||
count[i] += count[i - 1] | count[i] += count[i - 1] | ||
+ | '''return''' count | ||
− | /* принимает строку, для которой требуется построить суффиксный массив | + | <font color=darkgreen>/* принимает строку, для которой требуется построить суффиксный массив |
− | возвращает суффиксный массив */ | + | возвращает суффиксный массив */</font> |
− | ''' | + | '''int[]''' suff_array(str) |
s += '$' | s += '$' | ||
− | ALPHABET = 255 | + | ''ALPHABET'' = 255 |
− | // нулевая итерация | + | <font color=darkgreen>// нулевая итерация</font> |
− | count = '''int'''[max(ALPHABET, str.length)] | + | count = '''int'''[max(''ALPHABET'', str.length)] |
'''fill'''(count, 0) | '''fill'''(count, 0) | ||
'''for''' ch '''in''' str | '''for''' ch '''in''' str | ||
count[ch]++ | count[ch]++ | ||
− | '''calc_positions'''(count) | + | count = '''calc_positions'''(count) |
− | // suffs будет хранить индексы начал отсортированных подстрок текущей длины | + | <font color=darkgreen>// suffs будет хранить индексы начал отсортированных подстрок текущей длины</font> |
suffs = '''int'''[str.length] | suffs = '''int'''[str.length] | ||
'''for''' ch '''in''' str | '''for''' ch '''in''' str | ||
Строка 75: | Строка 76: | ||
classes[suf] = classesN; | classes[suf] = classesN; | ||
− | // нулевая итерация завершена | + | <font color=darkgreen>// нулевая итерация завершена |
− | // сортируем подстроки длиной 2 * cur_len = 2^k | + | // сортируем подстроки длиной 2 * cur_len = 2^k</font> |
curr_len = 1 | curr_len = 1 | ||
− | '''while''' cur_len < | + | '''while''' cur_len <tex> \leqslant </tex> str.length |
− | // сортируем по второй половине подстроки | + | <font color=darkgreen>// сортируем по второй половине подстроки</font> |
sorted_by2 = '''int'''[str.length] | sorted_by2 = '''int'''[str.length] | ||
'''for''' i = 0 .. str.length | '''for''' i = 0 .. str.length | ||
sorted_by2[i] = (suffs[i] + str.length - cur_len) % str.length | sorted_by2[i] = (suffs[i] + str.length - cur_len) % str.length | ||
− | // сортируем по первой половине | + | <font color=darkgreen>// сортируем по первой половине |
− | // сортировка устойчивая, значит получим целиком отсортированные подстроки | + | // сортировка устойчивая, значит получим целиком отсортированные подстроки</font> |
'''fill'''(count, 0) | '''fill'''(count, 0) | ||
'''for''' by2 '''in''' sorted_by2 | '''for''' by2 '''in''' sorted_by2 | ||
count[classes[by2]]++ | count[classes[by2]]++ | ||
− | '''calc_positions'''(count) | + | count = '''calc_positions'''(count) |
'''for''' i = 0 .. str.length | '''for''' i = 0 .. str.length | ||
suffs[count[classes[sorted_by2[i]]]++] = sorted_by2[i] | suffs[count[classes[sorted_by2[i]]]++] = sorted_by2[i] |
Версия 12:38, 31 мая 2015
Содержание
Постановка задачи
Дана циклическая строка
. Суффиксом циклической строки называется строка (будем называть такую строкую суффиксом под номером i). Требуется отсортировать все её суффиксы в порядке лексикографической сортировки.Решение
Рассматриваемый алгоритм состоит из
итераций. На -той итерации ( ) сортируются циклические подстроки длины . На последней, -ой итерации, будут сортироваться подстроки длины , что эквивалентно сортировке циклических сдвигов.На каждой итерации алгоритм помимо перестановки
индексов циклических подстрок будет поддерживать для каждой циклической подстроки длиной , начинающейся в позиции , номер класса эквивалентности , которому эта подстрока принадлежит. В самом деле, среди подстрок могут быть одинаковые, и алгоритму понадобится информация об этом. Кроме того, номера классов эквивалентности будем давать таким образом, чтобы они сохраняли и информацию о порядке: если один суффикс меньше другого, то и номер класса он должен получить меньший.Описание алгоритма
На нулевой итерации отсортируем циклические подстроки длины сортировки подсчетом построим массив , содержащий номера суффиксов, отсортированных в лексикографическом порядке. По этому массиву построим массив классов эквивалентности .
, т.е. первые символы строк, и разделим их на классы эквивалентности (одинаковые символы должны быть отнесены к одному классу эквивалентности). При помощиНа
-ой итерации имеем массивы и , вычисленные на предыдущей итерации. Приведем алгоритм, выполняющий -ую итерацию за . Поскольку итераций всего , такой алгоритм имеет асимптотику .Заметим, что циклическая подстрока длины
состоит из двух подстрок длины , которые мы можем сравнивать между собой за , используя информацию с предыдущей итерации — номера классов эквивалентности . Таким образом, для подстроки длины , начинающейся в позиции , вся необходимая информация содержится в паре чисел .Отсортируем подстроки длины цифровая сортировка: отсортируем пары сначала по вторым элементам, а затем по первым (устойчивой сортировкой). Однако вторые элементы уже упорядочены — этот порядок задан в массиве от предыдущей итерации. Тогда, чтобы получить порядок пар по вторым элементам, надо от каждого элемента массива отнять ( даёт упорядочение подстрок длины , и при переходе к строке вдвое большей длины эти подстроки становятся их вторыми половинками, поэтому от позиции второй половинки отнимается длина первой половинки).
по данным парам и запишем порядок в массив . Воспользуемся здесь приёмом, на котором основанаЧтобы произвести устойчивую сортировку по первым элементам пар, воспользуемся сортировкой подсчетом, имеющую асимптотику
.Осталось пересчитать номера классов эквивалентности
, пройдя по новой перестановке и сравнивая соседние элементы (как пары двух чисел).Пример
s = abacaba$
Псевдокод
/* преобразует масив count, так что
теперь он содержит позиции в массиве suffs с которых
необходимо вставлять подстроки, начинающиеся с соответствующих символов */
int[] calc_positions(count)
count[0] = 0
for i = 2 .. count.length
count[i] += count[i - 1]
return count
/* принимает строку, для которой требуется построить суффиксный массив
возвращает суффиксный массив */
int[] suff_array(str)
s += '$'
ALPHABET = 255
// нулевая итерация
count = int[max(ALPHABET, str.length)]
fill(count, 0)
for ch in str
count[ch]++
count = 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]]++
count = 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