字符串哈希.(BKDR_hash)

193 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

简介:

有许多哈希函数,其中很普遍,好用的一个就是BKDR函数.

BKDR_hash:

将string串转化为一个x进制的数字储存.

例如:

string T={1,2,3};

x=6;

image.png

扩展为:

string T={T1,T2,T3};

image.png

image.png

注意:

因为hash_code的值会很大会爆掉,所以我们去设一个数字M去取模.

我们都知道哈希函数其实是设一个映射关系,前者的集合里边的元素越多,那么映射后的集合里边的元素也就越多.

我们设的M可以理解为存放最后结果的容器的容量.

假设我们取模2,但是我们有三个不同的字符串等待哈希.那么取模后的结果无非就是0或1,只有两种可能,那必有两个不同的字符串的哈希值是相同的.

所以我们设的M要大于字符串的数量. 但取模的数不能乱取,不然会造成冲突(即两个字符串不相等,但取模后结果相同)。 这里我们用到一个小技巧,就是unsigned long long 如果溢出以后,它会自动帮你%2^64。 我们还可以从X(表示转换为X进制)来降低冲突。根据计算机科学家概率的方式来统计,发现X取:131,1331,13331… 它的冲突率是最小的。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const ll mod=1e9+7;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int N=1000010;
const int X=13331;
char T[N];
char S[N];
ull h[N],x[N],hs[N];
vector<ull>v;
void BKDR_hash(char s[])
{
    h[0]=s[0];
    x[0]=1;
    int ln=strlen(s);
    for(int i=1;i<ln;i++)
    {
        h[i]=h[i-1]*X+s[i];
        x[i]=x[i-1]*X;
    }
}
 
ull get_hash(int left,int right)
{
    if(!left)
        return h[right];
    else
        return h[right]-h[left-1]*x[right-left+1];
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    BKDR_hash(T);
    cin>>S;
    ull hs2=0;
    for(int i=0;i<strlen(S);i++)
        hs2=hs2*X+S[i];
    for(int i=0;i<strlen(T);i++)
        cout<<get_hash(i,min(strlen(T)-1,i+strlen(S)-1))<<" ";
    cout<<endl;
    cout<<hs2<<endl;
    //检测S串是否是T串的子串    通过对比hash_code值
}