Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа — различия между версиями
Vasin (обсуждение | вклад) (→Метод хеширования) |
Vasin (обсуждение | вклад) (→Метод хеширования) |
||
Строка 2: | Строка 2: | ||
==Метод хеширования== | ==Метод хеширования== | ||
− | Для решения задачи удобно использовать полиномиальный хеш, так его легко пересчитывать: <tex>hash(s[1..n]) = (p^{n - 1} s[1] + ... + p^{0} s[n]) | + | Для решения задачи удобно использовать полиномиальный хеш, так его легко пересчитывать: <tex>hash(s[1..n]) = (p^{n - 1} s[1] + ... + p^{0} s[n])</tex>, где <tex>p</tex> {{---}} это некоторое простое число, а <tex>r</tex> {{---}} некоторое большое число, чтобы было меньше коллизий (обычно берётся <tex>2^{32}</tex> или <tex>2^{64}</tex>, чтобы модуль брался автоматически при переполнении типов). Стоит обратить внимание, что если 2 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны. |
При удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки возможно за <tex>O(1)</tex>: | При удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки возможно за <tex>O(1)</tex>: | ||
− | <tex>hash(s[i + 1..i + m - 1]) = hash(s[i..i + m - 1]) - p^{m - 1} s[i]</tex>. | + | <tex>hash(s[i + 1..i + m - 1]) = (hash(s[i..i + m - 1]) - p^{m - 1} s[i]) mod r</tex>. |
− | <tex>hash(s[i + 1..i + m]) = p \cdot hash(s[i + 1..i + m - 1]) + s[i + m]</tex>. | + | <tex>hash(s[i + 1..i + m]) = (p \cdot hash(s[i + 1..i + m - 1]) + s[i + m]) mod r</tex>. |
− | Получается : <tex>hash(s[i + 1..i + m]) = p \cdot hash(s[i..i + m - 1]) - p^{m} s[i] + s[i + m]</tex>. | + | Получается : <tex>hash(s[i + 1..i + m]) = (p \cdot hash(s[i..i + m - 1]) - p^{m} s[i] + s[i + m]) mod r</tex>. |
+ | |||
+ | Следует учесть, что при получении отрицательного значения необходимо прибавить <tex>r</tex>. | ||
==Алгоритм== | ==Алгоритм== |
Версия 19:34, 8 июня 2012
Алгоритм Рабина-Карпа предназначен для поиска подстроки в строке.
Метод хеширования
Для решения задачи удобно использовать полиномиальный хеш, так его легко пересчитывать:
, где — это некоторое простое число, а — некоторое большое число, чтобы было меньше коллизий (обычно берётся или , чтобы модуль брался автоматически при переполнении типов). Стоит обратить внимание, что если 2 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны.При удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки возможно за
:.
.
Получается :
.Следует учесть, что при получении отрицательного значения необходимо прибавить
.Алгоритм
Алгоритм начинается с подсчета
и .Для
вычисляется и сравнивается с . Если они оказались равны — то считается, что подстрока входит в строку (начиная с позиции ) или проверяется, что подстрока является шаблоном, для этого выбираются и сравниваются случайные символы из строк.Для ускорения работы алгоритма оптимально предпосчитать
.Псевдокод
RabinKarp (s[1..n], p[1..m])
hp = hash(p[1..m])
h = hash(s[1..m])
for i = 1 to n - m + 1
if h = hp
answer.add(i)
h = p * h - p
* hash(s[i]) + hash(s[i + m])
if answer.size() == 0
return not found
else
return answer
Новый хеш
был получен с помощью быстрого пересчёта. Для сохранения корректности алгоритма нужно считать, что — пустой символ.Время работы
Изначальный подсчёт хешей выполняется за
. В цикле всего итераций — каждая выполняется за . Итоговое время работы алгоритма .Литература
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.