Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа — различия между версиями
Martoon (обсуждение | вклад) м (→Надёжность)  | 
				Martoon (обсуждение | вклад)   (Убрано про добавление r)  | 
				||
| Строка 11: | Строка 11: | ||
Получается : <tex>hash(s[i + 1..i + m]) = (p \cdot hash(s[i..i + m - 1]) - p^{m} s[i] + s[i + m]) \bmod r</tex>.  | Получается : <tex>hash(s[i + 1..i + m]) = (p \cdot hash(s[i..i + m - 1]) - p^{m} s[i] + s[i + m]) \bmod r</tex>.  | ||
| − | |||
| − | |||
==Алгоритм==  | ==Алгоритм==  | ||
| Строка 30: | Строка 28: | ||
                  answer.add(i)  |                   answer.add(i)  | ||
             h = (p * h - p<tex>^{m}</tex> * hash(s[i]) + hash(s[i + m])) mod r  |              h = (p * h - p<tex>^{m}</tex> * hash(s[i]) + hash(s[i + m])) mod r  | ||
| − | |||
| − | |||
        '''if''' answer.size() == 0  |         '''if''' answer.size() == 0  | ||
             '''return''' not found  |              '''return''' not found  | ||
Версия 19:11, 12 мая 2014
Алгоритм Рабина-Карпа предназначен для поиска подстроки в строке.
Содержание
Метод хеширования
Для решения задачи удобно использовать полиномиальный хеш, так его легко пересчитывать: , где — это некоторое простое число, а — некоторое большое число, для уменьшения числа коллизий (обычно берётся или , чтобы модуль брался автоматически при переполнении типов). Стоит обратить внимание, что если 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])) mod r
      if answer.size() == 0
           return not found
      else
           return answer
Новый хеш был получен с помощью быстрого пересчёта. Для сохранения корректности алгоритма нужно считать, что — пустой символ.
Время работы
Изначальный подсчёт хешей выполняется за . В цикле всего итераций, каждая выполняется за . Итоговое время работы алгоритма .
Надёжность
Если количество подстрок данной строки превышает количество хешей, то наступление коллизий неизбежно. Но даже при относительно небольших строках вероятность коллизий может быть высока, не говоря уже о способах составления специальных строк, где алгоритм на хешах выдаёт частые ложные срабатывания.
Например если взять , за принять строку Туэ-Морса[1] длиной , то алгоритм находит лишние вхождения почти в половине случаев.
Примечания
Литература
Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.