Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа — различия между версиями
Строка 3: | Строка 3: | ||
Давайте сначала определимся с методом хеширования. | Давайте сначала определимся с методом хеширования. | ||
− | Выберем полиномиальный хеш - <tex>hash(s[1..n]) = (p^{n - 1} s[1] + ... + p^{0} s[n])</tex> mod <tex>r</tex>, где <tex>p</tex> - это некоторое простое число, а <tex>r</tex> - некоторое большое число, чтобы было меньше коллизий (обычно берётся <tex>2^{32}</tex> или <tex>2^{64}</tex>, чтобы модуль брался автоматически - при переполнении типов;). Заметим, что если 2 строчки имеют одинаковый хэш, то они | + | Выберем полиномиальный хеш - <tex>hash(s[1..n]) = (p^{n - 1} s[1] + ... + p^{0} s[n])</tex> mod <tex>r</tex>, где <tex>p</tex> - это некоторое простое число, а <tex>r</tex> - некоторое большое число, чтобы было меньше коллизий (обычно берётся <tex>2^{32}</tex> или <tex>2^{64}</tex>, чтобы модуль брался автоматически - при переполнении типов;). Заметим, что если 2 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны. |
− | + | Давайте научимся при удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки за <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]</tex>. | ||
− | <tex>hash(s[i + 1..i + m]) = p \cdot hash(s[i + 1..i + m]) + s[i + m]</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..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]</tex>. | ||
Строка 19: | Строка 19: | ||
Давайте посчитаем <tex>hash(s[1..m])</tex> и <tex>hash(p[1..m])</tex>. | Давайте посчитаем <tex>hash(s[1..m])</tex> и <tex>hash(p[1..m])</tex>. | ||
− | И для <tex>i \in [1..n - m + 1]</tex> считаем <tex>hash(s[i..i + m - 1]</tex> - сравниваем с <tex>hash(p[1..m])</tex>. Если они получаются равными - то мы считаем, что подстрока <tex>p</tex> входит в строку <tex>s</tex> (начиная с позиции <tex>i</tex>;). | + | И для <tex>i \in [1..n - m + 1]</tex> считаем <tex>hash(s[i..i + m - 1]</tex> - сравниваем с <tex>hash(p[1..m])</tex>. Если они получаются равными - то мы считаем, что подстрока <tex>p</tex> входит в строку <tex>s</tex> (начиная с позиции <tex>i</tex>;) или мы проверяем, что подстрока является шаблоном, для этого выберем случайные символы из строк и сравним их. |
Следует предподсчитать - <tex>p^{m}</tex>. | Следует предподсчитать - <tex>p^{m}</tex>. |
Версия 00:16, 23 марта 2011
Алгоритм Рабина — Карпа — это алгоритм поиска строки, который ищет шаблон, то есть подстроку, в тексте используя хеширование.
Давайте сначала определимся с методом хеширования.
Выберем полиномиальный хеш -
mod , где - это некоторое простое число, а - некоторое большое число, чтобы было меньше коллизий (обычно берётся или , чтобы модуль брался автоматически - при переполнении типов;). Заметим, что если 2 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны.Давайте научимся при удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки за
:.
.
Получается :
.Теперь к самому алгоритму.
У нас сеть шаблон -
. У нас есть строка - . Мы хотим найти все вхождения шаблона в строку.Давайте посчитаем
и .И для
считаем - сравниваем с . Если они получаются равными - то мы считаем, что подстрока входит в строку (начиная с позиции ;) или мы проверяем, что подстрока является шаблоном, для этого выберем случайные символы из строк и сравним их.Следует предподсчитать -
.Псевдо-код:
1: function RabinKarp(string s[1..n], string p[1..m]) 2: hp := hash(p[1..m]) 3: h := hash(s[1..m])ы 4: for i from 1 to (n-m+1) 5: if h = hp 6: add i 7: h := ph - s[i] + s[i + m] 8: return not found
7 строка была получена с помощью быстрого пересчёта хеша. Мы считаем, что
- пустой символ.Посчитаем время работы.
Изначальный подсчёт хешей -
. В цикле всего итераций - каждая выполняется за . Итого - .