Поиск подстроки в строке с использованием хеширования. Алгоритм Рабина-Карпа — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Алгоритм)
Строка 1: Строка 1:
 
Алгоритм Рабина — Карпа — это алгоритм поиска подстроки в строке, используя хеширование.
 
Алгоритм Рабина — Карпа — это алгоритм поиска подстроки в строке, используя хеширование.
  
===Метод хеширования===
+
==Метод хеширования==
  
 
Выберем полиномиальный хеш - <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 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны.
Строка 13: Строка 13:
 
Получается : <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>.
  
===Алгоритм===
+
==Алгоритм==
  
 
У нас есть шаблон - <tex>p[1..m]</tex>. У нас есть строка - <tex>s[1..n]</tex>. Мы хотим найти все вхождения шаблона в строку.
 
У нас есть шаблон - <tex>p[1..m]</tex>. У нас есть строка - <tex>s[1..n]</tex>. Мы хотим найти все вхождения шаблона в строку.
Строка 23: Строка 23:
 
Следует предподсчитать <tex>p^{m}</tex>.
 
Следует предподсчитать <tex>p^{m}</tex>.
  
Псевдо-код:
+
==Псевдокод==
   '''1:''' function RabinKarp(string s[1..n], string p[1..m])
+
   '''RabinKarp''' ('''string''' <tex>s[1..n]</tex>, '''string''' <tex>p[1..m]</tex>)
  '''2:'''  hp := hash(p[1..m])
+
      <tex>hp \leftarrow hash(p[1..m])</tex>
  '''3:'''  h := hash(s[1..m])
+
      <tex>h \leftarrow hash(s[1..m])</tex>
  '''4:'''   for i from 1 to (n - m + 1)
+
      '''for''' <tex>i \leftarrow 1</tex> '''to''' <tex>n - m + 1</tex>
  '''5:'''       if h = hp
+
            '''if''' <tex>h = hp</tex>
  '''6:'''          answer.add(i)
+
                <tex>answer.add(i)</tex>
  '''7:'''      h := p <tex>\cdot</tex> h - <tex>p^{m}</tex>s[i] + s[i + m]
+
            <tex>h \leftarrow p \cdot h - p^{m} \cdot hash(s[i]) + hash(s[i + m])</tex>
  '''8:'''   if ans.size = 0  
+
      '''if''' <tex>ans.size = 0</tex>
  '''9:'''       return not found
+
            '''return''' <tex>not</tex> <tex>found</tex>
  '''10:''' else
+
      '''else'''
  '''11:'''     return answer
+
            '''return''' <tex>answer</tex>
  
7 строка была получена с помощью быстрого пересчёта хеша. Мы считаем, что <tex>s[n + 1]</tex> - пустой символ.
+
Новый хеш <tex>h</tex> был получен с помощью быстрого пересчёта. Мы считаем, что <tex>s[n + 1]</tex> - пустой символ.
  
Посчитаем время работы.
+
==Время работы==
  
 
Изначальный подсчёт хешей - <tex>O(m)</tex>. В цикле всего <tex>n - m + 1</tex> итераций - каждая выполняется за <tex>O(1)</tex>. Итого - <tex>O(n + m)</tex>.
 
Изначальный подсчёт хешей - <tex>O(m)</tex>. В цикле всего <tex>n - m + 1</tex> итераций - каждая выполняется за <tex>O(1)</tex>. Итого - <tex>O(n + m)</tex>.

Версия 22:02, 24 марта 2012

Алгоритм Рабина — Карпа — это алгоритм поиска подстроки в строке, используя хеширование.

Метод хеширования

Выберем полиномиальный хеш - [math]hash(s[1..n]) = (p^{n - 1} s[1] + ... + p^{0} s[n])[/math] mod [math]r[/math], где [math]p[/math] - это некоторое простое число, а [math]r[/math] - некоторое большое число, чтобы было меньше коллизий (обычно берётся [math]2^{32}[/math] или [math]2^{64}[/math], чтобы модуль брался автоматически - при переполнении типов;). Заметим, что если 2 строчки имеют одинаковый хэш, то они в большинстве таких случаев равны.

Давайте научимся при удалении первого символа строки и добавлении символа в конец считать хеш новой строки при помощи хеша изначальной строки за [math]O(1)[/math]:

[math]hash(s[i + 1..i + m - 1]) = hash(s[i..i + m - 1]) - p^{m - 1} s[i][/math].

[math]hash(s[i + 1..i + m]) = p \cdot hash(s[i + 1..i + m - 1]) + s[i + m][/math].

Получается : [math]hash(s[i + 1..i + m]) = p \cdot hash(s[i..i + m - 1]) - p^{m} s[i] + s[i + m][/math].

Алгоритм

У нас есть шаблон - [math]p[1..m][/math]. У нас есть строка - [math]s[1..n][/math]. Мы хотим найти все вхождения шаблона в строку.

Давайте посчитаем [math]hash(s[1..m])[/math] и [math]hash(p[1..m])[/math].

И для [math]i \in [1..n - m + 1][/math] считаем [math]hash(s[i..i + m - 1][/math] - сравниваем с [math]hash(p[1..m])[/math]. Если они получаются равными - то мы считаем, что подстрока [math]p[/math] входит в строку [math]s[/math] (начиная с позиции [math]i[/math];) или мы проверяем, что подстрока является шаблоном, для этого выберем случайные символы из строк и сравним их.

Следует предподсчитать [math]p^{m}[/math].

Псевдокод

 RabinKarp (string [math]s[1..n][/math], string [math]p[1..m][/math])
      [math]hp \leftarrow hash(p[1..m])[/math]
      [math]h \leftarrow hash(s[1..m])[/math]
      for [math]i \leftarrow 1[/math] to [math]n - m + 1[/math]
           if [math]h = hp[/math]
                [math]answer.add(i)[/math]
           [math]h \leftarrow p \cdot h - p^{m} \cdot hash(s[i]) + hash(s[i + m])[/math]
      if [math]ans.size = 0[/math]
           return [math]not[/math] [math]found[/math]
      else
           return [math]answer[/math]

Новый хеш [math]h[/math] был получен с помощью быстрого пересчёта. Мы считаем, что [math]s[n + 1][/math] - пустой символ.

Время работы

Изначальный подсчёт хешей - [math]O(m)[/math]. В цикле всего [math]n - m + 1[/math] итераций - каждая выполняется за [math]O(1)[/math]. Итого - [math]O(n + m)[/math].

Литература

Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.