Полиномиальные хеши на строках
Полиномиальная хэш-функция от строки $S$: $h(S) = S_0 + S_1 b + S_2 b^2 + S_3 b^3 + ... + S_N b^N$
Чтобы не происходило переполнения надо считать этот хеш по модулю большого простого числа $P$ (или, хотя бы числа взаимно простого с $b$). $h(S) = (S_0 + S_1 b + S_2 b^2 + S_3 b^3 + ... + S_N b^N) \mod P$
В качестве $P$ удобно использовать, например, простое число: $10^9+7$
Как найти подходяшие простые числа? Можно просто запомнить несколько подходящих. Или поискать с помощью программы.
Для поиска простых чисел можно использовать следующий код:
# -*- utf-8 -*-
# Полиномиальное хеширование строк
from math import *
def is_prime(a):
""" Простое ли число a? """
return all(a % i for i in range(2, int(a**0.5 + 0.1) + 1))
# all возвращает True только если все элементы множества не 0
# Генерируем все простые числа < 100
primes = [x for x in range(2, 100) if is_prime(x)]
print(*primes)
# Простые числа больше миллиарда
primes = [x for x in range(10 ** 9, 10 ** 9 + 1000) if is_prime(x)]
print(*primes)
Реализация на Rust
// Поиск простых чисел пробным делением: O(sqrt(n)) на одно число
// Простое ли число n: перебираем делители d, пока d * d <= n
pub fn is_prime(n: u64) -> bool {
if n < 2 {
return false;
}
let mut d = 2;
while d * d <= n {
if n % d == 0 {
return false;
}
d += 1;
}
true
}
fn main() {
// Простые числа меньше 100
let small: Vec<u64> = (0..100).filter(|&n| is_prime(n)).collect();
println!("{:?}", small);
// Количество простых чисел в промежутке [10^9, 10^9 + 1000)
let count = (1_000_000_000..1_000_001_000).filter(|&n| is_prime(n)).count();
println!("{}", count);
}
Реализация на C++
// Поиск простых чисел пробным делением: O(sqrt(n)) на одно число
#include <iostream>
// Простое ли число n: перебираем делители d, пока d * d <= n
bool is_prime(long long n) {
if (n < 2) return false;
for (long long d = 2; d * d <= n; ++d) {
if (n % d == 0) return false;
}
return true;
}
int main() {
// Простые числа меньше 100
for (long long n = 0; n < 100; ++n) {
if (is_prime(n)) std::cout << n << ' ';
}
std::cout << '\n';
// Количество простых чисел в промежутке [10^9, 10^9 + 1000)
int count = 0;
for (long long n = 1000000000LL; n < 1000001000LL; ++n) {
if (is_prime(n)) ++count;
}
std::cout << count << '\n';
return 0;
}
Пример вычисления хеша на C++
const int p = 31; long long hash = 0, p_pow = 1; for (size_t i=0; iРеализация на Python
# Полиномиальный хеш строки h(S) = S_0 + S_1*b + S_2*b^2 + ... mod P: O(|S|) B = 31 # основание b, как в примере на C++ P = 10 ** 9 + 7 # большое простое число def poly_hash(s): """Хеш строки: буква 'a' дает 1, 'b' - 2 и т.д., чтобы у строки 'aaa' хеш был ненулевым.""" result = 0 power = 1 # b^i mod P for ch in s: result = (result + (ord(ch) - ord("a") + 1) * power) % P power = power * B % P return result if __name__ == "__main__": print(poly_hash("abc")) # 1 + 2*31 + 3*31^2 = 2946 print(poly_hash("ab")) # 1 + 2*31 = 63 print(poly_hash("ba")) # 2 + 1*31 = 33Реализация на Rust
// Полиномиальный хеш строки h(S) = S_0 + S_1*b + S_2*b^2 + ... mod P: O(|S|) pub const B: u64 = 31; // основание b, как в примере на C++ pub const P: u64 = 1_000_000_007; // большое простое число // Хеш строки: буква 'a' дает 1, 'b' - 2 и т.д., чтобы у строки "aaa" хеш был ненулевым pub fn poly_hash(s: &str) -> u64 { let mut result = 0; let mut power = 1; // b^i mod P for ch in s.bytes() { let value = ch as u64 - b'a' as u64 + 1; result = (result + value * power) % P; power = power * B % P; } result } fn main() { println!("{}", poly_hash("abc")); // 1 + 2*31 + 3*31^2 = 2946 println!("{}", poly_hash("ab")); // 1 + 2*31 = 63 println!("{}", poly_hash("ba")); // 2 + 1*31 = 33 }