Задача: “Дается строка S. Длина S не превышает 1e5. Необходимо найти минимальный в лексикографическом порядке циклический сдвиг строки S”.
1.Полиномиальные хеш-функции.
По мотивам презентации Александра Скиданова
Рассмотрим строку S = “abcdabb”
h(L) – полиномиальная хеш-функция от префикса строки S длины L.
h(0) = h(“”) = 0
h(1) = h(“a”) = a
h(2) = h(“ab”) = a*x1 + b
h(3) = h(“abc”) = a*x2 + b*x1 + c
h(4) = h(“abcd”) = a*x3 + b*x2 + c*x1 + d
h(5) = h(“abcda”) = a*x4 + b*x3 + c*x2 + d*x1 + a
h(5) = h(“abcdab”) = a*x5 + b*x4 + c*x3 + d*x2 + a*x1 + b
h(5) = h(“abcdabb”) = a*x6 + b*x5 + c*x4 + d*x3 + a*x2 + b*x1 + b
Рекурсивно полиномиальную хеш-функцию h(L) можно выразить так:
H(a,b)- полиномиальная хеш-функция от подстроки S в интервале [a,b)
Следовательно можно получать полиномиальные хеш-функции от любой подстроки S за O(1).
Пусть len – длина строки S.
Возвращаемся к первоначальной задачи. Чтобы найти минимальный циклический сдвиг строки S:
1) Модифицируем исходную строчку S = S + S.
2) Считаем полиномиальную хеш-функцию h(L) для всех префиксов строки S.
3) На текущем шаге наилучший ответ на поставленную задачу – это подстрока строки S длины len, начинающаяся c позиции best = 0.
4) Последовательно перебираем все возможные начальные позиции cur в интервале [1,len)
5) Бинарным поиском подбираем длину общего префикса для наилучшего текущего ответа, начинающегося с позиции best, и кандидата на это место, начинающегося с позиции cur.
6) Сравниваем первые различные символы двух образцов. Если у текущего кандидата на этой позиции стоит символ с меньшим кодом, тогда обновляет наилучший ответ.
- typedef unsigned long long UINT;
- int min_cycle_shift(char *s, int init_len) {
- UINT *hash = new UINT[2*init_len];
- hash[0] = 0;
- for (int i=1;i<2*init_len;++i)
- hash[i] = x * hash[i-1] + s[i-1];
-
- int bst_pos = 0;
- for (int cur_pos = 1; cur_pos < init_len; ++cur_pos) {
- int l = 0, r = init_len-1, res = 0;
- while (l <= r) {
- int len = (l + r) >> 1;
- UINT hash_bst = hash[bst_pos + len] - hash[bst_pos] * _pow[len];
- UINT hash_cur = hash[cur_pos + len] - hash[cur_pos] * _pow[len];
- if (hash_bst == hash_cur) {
- res = len;
- l = len + 1;
- }
- else {
- r = len - 1;
- }
- }
- if (s[bst_pos + res] > s[cur_pos + res])
- bst_pos = cur_pos;
- }
- delete[] hash;
- return bst_pos + 1;
- }
* This source code was highlighted with Source Code Highlighter.
Сложность данного решения: |
P.S: В качестве x нужно использовать небольшое простое число. Например 11.
P.S.S: Все вычисления ведутся без модульной арифметики. Поэтому интервал возможных значений полиномиальной хеш-функции равен 264.
2. Алгоритм Дюваля
Все что нужно - написано на e-maxx’e. Приведу просто фрагмент кода, решающую ту же самую задачу.
- // s уже равна s+s
- int min_cycle_shift(char *s, int len) {
- int i = 0, ans = 0;
- while (i < len/2) {
- ans = i;
- int k = i, j = i+1;
- while (j < len && s[k] <= s[j]) {
- if (s[k] < s[j])
- k = i;
- else
- ++k;
- ++j;
- }
- while (i<=k)
- i += j - k;
- }
- return ans + 1;
- }
* This source code was highlighted with Source Code Highlighter.
Сложность данного решения:
Комментариев нет:
Отправить комментарий