連続部分列の hash を O(1) で取得可能なデータ構造。
using ull = unsigned long long;
vector<ull> powr; // 基数の累乗
class RollingHash {
public:
RollingHash(const string &s){
if(base == 0){
// base を 2^61-1の原始根 & 1e9以上 で初期化
random_device rnd;
mt19937 mt(rnd());
uniform_int_distribution<ull> rand(1,INT_MAX);
while(1){
ull k = rand(mt);
base = modPow(37,k);
if(euclid(Mod-1,k) == 1 && base > 1e9)break;
}
}
powr.assign(1,1);
int size = s.size();
hash.assign(size+1,0);
// 左端~の hash 前計算
for(int i=0; i<size; i++){
hash[i+1] = mulMod(hash[i],base) + s[i];
hash[i+1] = getMod(hash[i+1]);
}
}
ull get(int l, int r){
ull res = hash[r] - mulMod(hash[l],getPow(r-l));
return getMod(res + Positivizer);
}
private:
static const ull Mod = (1LL << 61) - 1;
static const ull Positivizer = Mod * 7;
static ull base; // 基数
vector<ull> hash; // hash の cash
static ull getPow(int n){
while(powr.size() <= n){
// 次の基数の累乗を求める
powr.push_back(mulMod(powr.back(), base));
}
return powr[n];
}
static ull mulMod(ull x,ull y){
// Fermatの小定理 & 2進数のmod を利用
__int128_t t = (__int128_t) x*y;
t = (t >> 61) + (t & Mod);
if(t >= Mod)t -= Mod;
return (ull)t;
}
static ull getMod(ull val){
val = (val >> 61) + (val & Mod);
if(val >= Mod)val -= Mod;
return (ull)val;
}
static ull euclid(ull a,ull b){
if(b != 0)return euclid(b,a%b);
else return a;
}
static ull modPow(ull a, ull b) {
ll res= 1;
for(; b; a=getMod(a*a), b>>=1) {
if(b & 1)res = getMod(res*a);
}
return res;
}
};
ull RollingHash::base;