← Назад к алгоритмам со строками

Хеширование строк

Поиск всех вхождений подстроки в строку за O(n + m)

🐍 Python
mod = 9223372036854775807

# Модульная арифметика
def add(a, b):
    if a + b >= mod:
        return a + b - mod
    return a + b

def sub(a, b):
    if a - b < 0:
        return a - b + mod
    return a - b

def mul(a, b):
    return a * b % mod

p = 31
s1 = input()
n = len(s1)
s2 = input()
m = len(s2)

H = [0] * (n + 1)
PU = [0] * (n + 1)
PU[0] = 1

for i in range(n):
    x = ord(s1[i]) - ord('a') + 1
    H[i + 1] = add(mul(H[i], p), x)
    PU[i + 1] = mul(PU[i], p)

h = 0
for i in range(m):
    x = ord(s2[i]) - ord('a') + 1
    h = add(mul(h, p), x)

A = []
for i in range(m - 1, n):
    r = i
    l = r - m + 1
    if sub(H[r + 1], mul(H[l], PU[r - l + 1])) == h:
        A.append(l)

print(len(A))
print(*[x + 1 for x in A])
⚙️ C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll mod = 9223372036854775807;

ll add(ll a, ll b) {
    if (a + b >= mod) return a + b - mod;
    return a + b;
}

ll sub(ll a, ll b) {
    if (a - b < 0) return a - b + mod;
    return a - b;
}

ll mul(ll a, ll b) {
    return (__int128)a * b % mod;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    const ll p = 31;
    
    string s1, s2;
    cin >> s1 >> s2;
    
    ll n = s1.size();
    ll m = s2.size();
    
    vector<ll> H(n + 1, 0);
    vector<ll> PU(n + 1, 0);
    PU[0] = 1;
    
    for (ll i = 0; i < n; i++) {
        ll x = s1[i] - 'a' + 1;
        H[i + 1] = add(mul(H[i], p), x);
        PU[i + 1] = mul(PU[i], p);
    }
    
    ll h = 0;
    for (ll i = 0; i < m; i++) {
        ll x = s2[i] - 'a' + 1;
        h = add(mul(h, p), x);
    }
    
    vector<ll> A;
    for (ll i = m - 1; i < n; i++) {
        ll r = i;
        ll l = r - m + 1;
        if (sub(H[r + 1], mul(H[l], PU[r - l + 1])) == h) {
            A.push_back(l);
        }
    }
    
    cout << A.size() << "\n";
    for (ll x : A) {
        cout << x + 1 << " ";
    }
    
    return 0;
}

Как работает хеширование строк?

Полиномиальное хеширование — это метод, который позволяет быстро вычислять хеш любой подстроки и сравнивать строки за O(1).

Основная идея: Строка представляется как число в системе счисления с основанием p (31), а хеш подстроки — как значение этого числа по модулю mod.

Определение хеша

Хеш строки S длины L: hash(S) = (S[0] × p^{L-1} + S[1] × p^{L-2} + ... + S[L-1] × p^{0})

Предподсчёт

Хеш подстроки

Формула: hash(s1[l..r]) = H[r+1] - H[l] × p^{r-l+1}

Алгоритм поиска подстроки

  1. Вычисляем хеш образца s2
  2. Вычисляем хеши всех префиксов строки s1
  3. Проходим по всем позициям, где может начинаться образец (0..n-m)
  4. Сравниваем хеш образца с хешем подстроки s1[i..i+m-1]
  5. При совпадении добавляем позицию в ответ

Временная сложность: O(n + m) — построение хешей и один проход по строке.

Пространственная сложность: O(n) для хранения хешей и степеней.

Внимание: При использовании данного модуля (9223372036854775807, простое число) вероятность коллизии крайне мала. Для повышения надёжности можно использовать два разных модуля.

Формат ввода и вывода

Ввод:

Вывод:

Пример ввода:

abacabadabacaba
aba

Вывод:

4
1 5 9 13

Пояснение: образец "aba" встречается в позициях 1, 5, 9, 13 (1-индексация).

Попробовать хеширование строк онлайн

Введите текст и образец для поиска:

Результат появится здесь...