- 使用自然溢出时数据量不应太大,因为要用到map,(数组不够开ull!),map会自动排序,所以会超时。所以超过5*10^5就别用自然溢出了,找到最大可能值即可。
- 一般乘以素数p(131,13331,2333等)或最大值,然后%素数1e9+7
字符串哈希
即将一个字符串转化成一个整数,并保证字符串不同,得到的哈希值不同,这样就可以用来判断一个该字串是否重复出现过。
(如果直接把string当做键,则每次在map中查找时要一个一个字符地找,跟存在数组中每区别,比较数值当然更快。)
由哈希函数的性质,对于一个字符串:S=s1s2...sn,我们把每个字符转换成idx(si)=si-'a'+1 当然直接用字符串的ASCII码表示也可以,则哈希模型为Hash(i)=Hash(i-1)*p+idx(si),其中p为素数。最终算出的Hash(n)作为该字符串的哈希值。所以构造哈希函数的关键点在于使不同字符串的哈希冲突率尽可能小。下面介绍几种哈希函数:
哈希方法
- 自然溢出方法:利用unsigned long long 自然溢出,相当于自动对2^64−1取模
定义哈希函数:unsigned long long hash[n];
公式:Hash[i] = Hash[i-1]*p+idx(si)
- 单哈希方法
公式:Hash[i] = (Hash[i-1]*p+idx(si))%mod
p,mod均为质数,p<mod,p,mod取尽量大时冲突很小
- 双哈希方法:将字符串用不同mod单哈希两次,结果用二元组表示
Hash1[i] = (Hash1[i-1]*p+idx(si))%mod
Hash2[i] = (Hash2[i-1]*p+idx(si))%mod
Hash[i]:<Hash1[i],Hash2[i]>
这种方法很安全
求出一个串的哈希值后得到字串的哈希值:o(1)
素数的选择
像1e9+7等常见素数很可能被出题人卡,所以可以选择一些其他的素数,像2333等
下面求两个串中同字母异构的个数,不关心排列顺序,所以哈希时从a~z遍历取个数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define far(i,t,n) for(int i=t;i<n;++i)
char a[4050],b[4050];
map<ull,ll>hashS;
map<ull,ll>::iterator it;
ull cnt[30];
ull p=1e9+7;
ull getHash()
{
ull ans=26;
far(i,0,26)
{
ans*=p;
ans+=cnt[i];
}
return ans;
}
int main()
{
scanf("%s%s",a,b);
int la=strlen(a),lb=strlen(b);
int l=min(la,lb);
int ans=0;
for(int i=1;i<=l;++i)
{
hashS.clear();
memset(cnt,0,sizeof(cnt));
for(int j=0;j<i;j++)
cnt[a[j]-'a']++;
for(int j=0;i+j<la;++j)
{
hashS[getHash()]++;
cnt[a[j]-'a']--;
cnt[a[i+j]-'a']++;
}
hashS[getHash()]++;
memset(cnt,0,sizeof(cnt));
for(int j=0;j<i;j++)
cnt[b[j]-'a']++;
for(int j=0;i+j<lb;++j)
{
if(hashS[getHash()])
ans=max(ans,i);
cnt[b[j]-'a']--;
cnt[b[i+j]-'a']++;
}
if(hashS[getHash()])ans=max(ans,i);
}
printf("%d\n",ans);
return 0;
}
如果需要算出一个字符串长度为n的所有子串,则提前预处理出0~i的哈希值hash[i],则可以以0(1)的复杂度算出[l,r]的哈希值
#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
char t[1000010];
char w[10010];
const int p=12582917;
ull po[10010];
ull Hash[1000010];
void initPo()//预处理p^(r-l+1)
{
po[0]=1;
po[1]=p;
far(i,2,10002)
po[i]=po[i-1]*p;
}
ull getSingleHash(char c[],int n)
{
Hash[0]=c[0]-'A';
far(i,1,n)
{
Hash[i]=Hash[i-1]*p;
Hash[i]+=c[i]-'A';
}
}
ull getHash(int l,int r)
{
if(l==0)
return Hash[r];
return Hash[r]-Hash[l-1]*po[r-l+1];
}
int main()
{
int Kase;
cin>>Kase;
initPo();
while(Kase--)
{
scanf("%s%s",w,t);
int lw=strlen(w),lt=strlen(t);
ull x=w[0]-'A';
far(i,1,lw)
x=x*p+(w[i]-'A');
getSingleHash(t,lt);
int ans=0;
for(int i=0;i<=lt-lw;++i)
{
// cout<<i<<" "<<getHash(i,i+lw)<<endl;
if(x==getHash(i,i+lw-1))
++ans;
}
printf("%d\n",ans);
}
return 0;
}
参考:https://blog.csdn.net/pengwill97/article/details/80879387