Полиномиальное хешированиеПусть – это строка длины , тогда полиномиальный хеш от строки вычисляется следующим образом: где и – это некоторые константы. Обычно число выбирается так, чтобы оно было простым и не меньшим максимального из кодов символов строки.
Хеши обладают следующим свойством: у одинаковых строк хеши обязательно равны. Поэтому, если хеши для некоторых строк уже вычислены, то мы можем сравнить их за . Однако, с некоторой вероятностью мы получим неверный результат, т.е. произойдет коллизия. Поэтому с помощью полиномиального хеширования решаются те задачи, в которых вероятность коллизий мала, иначе при совпадении хешей строки нужно дополнительно проверять на равенство. Алгоритм Рабина-КарпаВ качестве примера применения полиномиального хеширования рассмотрим задачу поиска подстроки в строке. Пусть нам требуется найти все вхождения строки длины в строку длины . Будем считать, что . Вычислим сначала хеши для всех префиксов строки и запишем их в массив . То есть – это хеш для подстроки . Тогда мы можем за вычислить хеш , домноженный на , любой подстроки из формулы Вычислим теперь хеш для строки . Осталось пройти по всем подстрокам длины , вычисляя их хеши и сравнивая с хешем .
Реализация алгоритма на языке С++: std::vector<unsigned long long> pows(n1);
pows[0] = 1;
for (int i = 1; i < (int)pows.size(); i++) {
pows[i] = pows[i - 1] * P;
}
std::vector<unsigned long long> hashes(n1 + 1);
hashes[0] = 0;
for (int i = 1; i < (int)hashes.size(); i++) {
hashes[i] = hashes[i - 1] + s1[i - 1] * pows[i - 1];
}
unsigned long long s2Hash = 0;
for (int i = 1; i <= n2; i++) {
s2Hash += s2[i - 1] * pows[i - 1];
}
std::vector<int> positionsOfs2;
for (int i = 0; i + n2 <= n1; i++) {
if (pows[i] * s2Hash == hashes[i + n2] - hashes[i]) {
positionsOfs2.push_back(i);
}
}
В реализации взятие по модулю опускается, то есть вычисления производятся по модулю , где зависит от используемого типа. Сложность алгоритма равна .
|
|||
|
|||
|