字符串哈希(Hash)

基础概念

作用: 快速判断两个串是否相等,判断一个字符串是否出现过。
更多应用可以看这篇好文

字符串哈希实质上就是把每个不同的字符串转成不同的整数。
可以发现,与一个string有关的HASH值不仅仅跟每个字符的个数有关,还和字符所在的位数有关。

比如说对于字符串:abc,base设为32
\(a=hash[1]=a-'a'+1=1\)
\(ab=hash[2]=hash[1]∗32+b-'a'+1=32+2=34\)
\(abc=hash[3]=hash[2]∗32+c-'a'+1=1088+3=1091\)
也就是说:abc,可以映射为整数1091
也就是把字符串看成32进制的一个数。

通过简单的思考,即可知道转化公式为:
hash[i]=hash[i−1]∗base+idx(s[i]);

注意:

  1. 为了尽量避免冲突,base建议取:131/1331

  2. 为了方便将变量定义为unsigned long long。
    当存储的数据大于unsigned long long的存储范围时,会自动mod 264−1,就不用mod其他质数来保证唯一性了。

138. 兔子与兔子

模板题
题意:
每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子 DNA 序列是否一模一样。
代码:

#include <bits/stdc++.h>
#define ins 0x3f3f3f3f
#define pii pair<int, int>
#define ull unsigned  long long 
using namespace std;
const int N = 1000001,base=131;

ull h[N],p[N];
char str[N];
ull get(int l,int r){
    return h[r]-h[l-1]*p[r-l+1];
}
void build(){
    int n=strlen(str+1);
    p[0]=1;
    for(int i=1;i<=n;i++){
        h[i]=h[i-1]*base+str[i]-'a'+1;
        p[i]=p[i-1]*base;
    }
}
void solve()
{
    cin>>str+1;
    build();
    int n;
    cin >> n;
    int l1,r1,l2,r2;
    for(int i=0;i<n;i++){
        cin>>l1>>r1>>l2>>r2;
        if(get(l1,r1)==get(l2,r2)) puts("Yes");
        else puts("No");
    }
    
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}

双哈希

思想:
按照不同的模数分别进项哈希,然后分别比较。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const ull mod1 = 1e9 + 7, mod2 = 2147483647;
const int N = 4e5 + 10;
const int p = 131;
ull a1[N], a2[N], h1[N], h2[N];
pair<ull, ull> res[N];
map<pair<ull, ull>, ull> mp;
void hash1(string s)
{ //预处理hash函数前缀和
    a1[0] = 1;
    int n = s.size();
    for (int i = 1; i <= n; i++)
    {
        a1[i] = a1[i - 1] * p % mod1;
        h1[i] = (h1[i - 1] * p % mod1 + (s[i - 1] - 'a' + 1)) % mod1;
    }
}
void hash2(string s)
{ //预处理hash函数前缀和
    a2[0] = 1;
    int n = s.size();
    for (int i = 1; i <= n; i++)
    {
        a2[i] = a2[i - 1] * p % mod2;
        h2[i] = (h2[i - 1] * p % mod2 + (s[i - 1] - 'a' + 1)) % mod2;
    }
}
ull get1(int l, int r)
{ //计算s[l--r]的hash值
    return (h1[r] - h1[l - 1] * a1[r - l + 1] % mod1 + mod1) % mod1;
}
ull get2(int l, int r)
{ //计算s[l--r]的hash值
    return (h2[r] - h2[l - 1] * a2[r - l + 1] % mod2 + mod2) % mod2;
}
int main()
{
    int n;
    cin >> n;
    string s;
    int pos = 0;
    while (n--)
    {
        cin >> s;
        int len = s.size();
        hash1(s);
        hash2(s);
        mp[{h1[len], h2[len]}]++;
        for (int i = 1; i + i < len; i++)
        { //寻找字符串s中前部分和后部分相同的子串
            if (get1(1, i) == get1(len - i + 1, len) && get2(1, i) == get2(len - i + 1, len))
            {
                res[pos++] = {get1(i + 1, len - i), get2(i + 1, len - i)};
            }
        }
    }
    ll ans = 0;
    for (int i = 0; i < pos; i++)
    { //统计与子串相同的字符串
        ans += mp[res[i]];
    }
    for (auto it : mp)
    { //统计相同字符串
        ans += it.second * (it.second - 1) / 2;
    }
    cout << ans << endl;
    return 0;
}

posted @ 2022-07-28 20:31  kingwzun  阅读(1823)  评论(0)    收藏  举报