Appearance
此处 Hash 指字符串哈希,只用来判断两个字符串是否相同。
字符串哈希,一般使用多项式哈希,将字符串
单哈:
将字符串
在把不同字符串的哈希值近似看作独立、均匀分布于
这里描述的是
例如,对于大小约为 int 模数时应将具体的 long long 上界的模数,两个余数相乘可能超过 long long,需要使用 __int128 或其他安全的模乘方法。
自然溢出:
自然溢出是指使用 unsigned long long 进行运算,结果会自动对 __int128,并且运算速度较快。
但是由于模数的特殊性,非常容易构造哈希冲突。
双哈:
将字符串
若两个哈希使用相同的底数和字符映射,先在整数上定义同一个多项式
若两个哈希使用不同底数或不同字符映射,它们不再是同一个整数多项式在不同模数下的余数,因此不能使用上述 CRT 等价关系。只有在把两个哈希近似看作相互独立且均匀分布时,才能估计一对不同字符串同时碰撞的概率约为
双哈只能显著降低随机数据下的碰撞概率,不能保证完全无碰撞;面对可能刻意构造的数据,还需要随机化底数、模数或字符映射。
最简单的随机化方式是为每个字符随机映射一个权值,而不是固定使用
点击展开代码
cpp
const int Base[4] = {3333, 13331, 131, 13331},
mod[4] = {1610612741, 1061067769, (int)1e9 + 7, (int)1e9 + 9};
int m1 = 0, m2 = 1;
void init(int n) {
base[0][0] = base[0][1] = 1;
for (int i = 1; i <= n; i++) {
base[i][0] = 1ll * base[i - 1][0] * Base[m1] % mod[m1];
base[i][1] = 1ll * base[i - 1][1] * Base[m2] % mod[m2];
}
}
void init(int a[], int n) {
for (int i = 1; i <= n; i++) {
sum[i][0] = (1ll * sum[i - 1][0] * Base[m1] + a[i]) % mod[m1];
sum[i][1] = (1ll * sum[i - 1][1] * Base[m2] + a[i]) % mod[m2];
}
}
u64 sum_hash(int l, int r) {
int now = (sum[r][0] - 1ll * sum[l - 1][0] * base[r - l + 1][0]) % mod[m1];
int cur = (sum[r][1] - 1ll * sum[l - 1][1] * base[r - l + 1][1]) % mod[m2];
if (now < 0) // 注意负数不能 u64
now += mod[m1];
if (cur < 0)
cur += mod[m2];
return (1ull * now) << 32 | cur;
}注:因为 pair<int,int> 是两个 int,若要使用 map 储存字符串的哈希值,将 pair<int,int> 压成 long long 后比较哈希值的常数会更小。
