跳转至

字符串的hash

约 210 个字 49 行代码 预计阅读时间 1 分钟

字符串映射为整数,O(1) 比较字符串

原理

hash=\sum\limits_{i=1}^n base^{n-i}a_i

把字符串看成 base 进制数,并模一个大数解决溢出

0<a_i<base<mod,gcd(base,mod)=1

例如把只有小写字母的字符串的 a_i设为 s_i-'a'+1

这些限制都是为了减少冲突可能性

base可以取131或13331,冲突小

大数不可以取 2^{64} (即ull自然溢出),因为会被卡掉

大数最好用大质数,可以取1e9+9

或者使用双模数(即使用两个模数对一个字符串计算两遍哈希值,根据中国剩余定理,这可以使哈希范围扩大到两数乘积

当然可以类似的使用更多模数

typedef unsigned long long ull;
const int N=1e6+1,modnum=2;
const ull base=131,mod[modnum]={(ull)1e8+7,(ull)1e9+9};
ull pw[modnum][N];
void init()
{
    for(int j=0;j<modnum;++j)
    {
        pw[j][0]=1;
        for(int i=1;i<N;++i)
            pw[j][i]=pw[j][i-1]*base%mod[j];
    }
}
void initHash(string &s,vector<vector<int>> &h)
{
    h.resize(modnum);
    for(int j=0;j<modnum;++j)
    {
        h[j].resize(s.size()+1);
        h[j][0]=0;
        for(int i=0;i<s.size();++i)
            h[j][i+1]=(h[j][i]*base+s[i]-'a'+1)%mod[j];
    }
}
array<ull,modnum> subHash(vector<vector<int>> &h,int l,int r)
{
    array<ull,modnum> res;
    for(int i=0;i<modnum;++i)
        res[i]=(h[i][r]+mod[i]-h[i][l-1]*pw[i][r-l+1]%mod[i])%mod[i];
    return res;
}

判断回文串

原哈希值和reverse哈希值一样

void initHash(string &s,vector<vector<int>> &rev)
{
    rev.resize(modnum);
    for(int j=0;j<modnum;++j)
    {
        rev[j].resize(s.size()+1);
        rev[j][0]=0;
        for(int i=0;i<s.size();++i)
            rev[j][i+1]=(rev[j][i]*base+s[s.size()-1-i]-'a'+1)%mod[j];
    }
}
array<ull,modnum> subHash(vector<vector<int>> &rev,int l,int r)
{
    array<ull,modnum> res;
    for(int i=0;i<modnum;++i)
        res[i]=(rev[i][rev[0].size()-l]+mod[i]-rev[i][rev[0].size()-1-r]*pw[i][r-l+1]%mod[i])%mod[i];
    return res;
}