используя хитрый прием, чтобы найти строки, которые вероятно равны.

Определение «хеш-чисел» для строк может облегчить сравнение за счет точности.

Мотивация

Предположим, у нас есть очень длинная строка M и строка пароля p, и мы хотим подсчитать, сколько раз пароль появляется в M.

наивный алгоритм: перейти к каждому символу в M и сформировать строку с последующими символами, проверяя, равна ли сформированная подстрока коду доступа. Время выполнения, естественно, O (n²).

Чтобы сделать это за O(n) (вроде бы), мы используем хеширование.

Алгоритм

Мы определяем хеш-номер строки p как (p[0]*A^n+p[1]*A^n-1…+p[n-1]*A⁰) по модулю B, где p[ind] — это инд-ый символ и A, B — произвольные числа.

Естественно, две одинаковые строки будут иметь общий хеш-номер, и если две строки имеют разные номера, они тоже будут различаться. Однако может случиться так, что две разные строки имеют общий хеш-номер, и это просто потому, что количество возможных чисел всегда будет меньше, чем количество строк.

Быстрый способ вычисления хеш-чисел для подстрок

Мы можем использовать мощь массивов префиксов для эффективного вычисления хэш-чисел для любой подстроки A.

По сути, мы храним в h[i] хеш-номер для M[0…i], что можно сделать с помощью этой рекурсии:

  • h[0]=M[0]
  • h[j] = (h[j-1]A+S[j])%B, для j›0

Однако этого недостаточно, чтобы вычислить хеш-число для произвольного диапазона M[x…y], для этого нужен еще один массив k, где k[x]=(A^x)%B.

Теперь M[x…y] просто: (h[y]−h[x−1]*k[y−x+1]) %B.

Вернемся к исходной проблеме

Итак, теперь, когда у нас есть эффективный способ вычисления хеш-чисел, как это упрощает задачу? Что ж, мы можем выполнить итерацию M в режиме скользящего окна, чтобы собрать все подстроки с той же длиной и значением хеш-функции, что и p. Получив эти строки, мы можем проверить, действительно ли они равны p.

Это вероятно уменьшает время работы алгоритма, поскольку вероятность того, что две разные строки имеют одинаковый хеш-код, становится очень малой по мере увеличения длины B.