一种简单的子串 Hash 取模优化方法
我们经常用 hash 表来定位子串,或者希望 hash 有更高的正确率,此时 32 位的模数是不够的。通常使用的方法是模两个小素数,而这并不非常方便。
注意到模数是任取的,我们取素数 \(M = 2^{61}-1\) 作为模数。
此时对 \(M\) 取模相当容易,只要把 \(x\) 的高 \(61\) 位和低 \(61\) 位相加,再判断是否大于等于 \(M\) 即可。
对于底数 \(b\) 我们再预处理出 \(-b^i \bmod M\),就可以快速提取区间 hash 了。由于存在将两个 64 位的数相乘并存入两个寄存器中的指令,计算乘法速度并不慢。
const ull HashM = (1ULL << 61) - 1;
ull red(u128 x) {
ll v = ull(x & HashM) + ull(x >> 61) - HashM;
return v + (HashM & (v >> 63));
}