字符串哈希
字符串哈希
取一固定值\(P\),把字符串\(S\)看做\(P\)进制数,并分配一个大于\(0\)的数值,代表每种字符。取一固定值\(Mod\),求出该\(P\)进制数对的\(Mod\)余数,作为该字符串的\(Hash\)值。
注意点:
1、需要给字符分配一个大于\(0\)的值(比如:如果'A'→0,那么"AA"→0,这样就没法区分了).
2、通常(个人习惯)\(P=13331\),\(Mod=2^{64}\),这种情况下
值产生冲突的概率极低(采用\(M=2^{64}\)时,直接使用\(unsigned\) \(long\) \(long\)自然溢出就可以).
计算某个字符串的哈希值
ll makehash(string x){
ll hashv=0;
for(auto i:s){
hashv=(hashv*p+i)%mod;
}
return hashv;
}
查询某个子串的哈希值
如果我们已知字符串\(S\)的哈希值为\(HashS\),字符串\(S+T\)的哈希值为\(HashST\),那么字符串\(T\)的哈希值为
这就相当于通过\(P\)进制下在\(S\)后边补\(0\)的方式,把\(S\)左移到与\(S+T\)的左端对齐,然后二者相减就得到了\(HashT\).
typedef unsigned long long ll;
const ll maxn=1e5+5,p=13331;
ll hash[maxn],po[maxn];
string s;
void init(){
po[0]=1;
for(ll i=1;i<=n;i++){
hash[i]=hash[i-1]*p+s[i-1];
p[i]=p[i-1]*p;
}
}
ll gethash(ll l,ll r){
return hash[r]-hash[l-1]*po[r-l+1];
}
双哈希
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll maxn=1e5+5,p=13331,mod=1e9+7;//第二个模数取2^64,直接用unsigned long long自然溢出
char s[maxn];
ll n,h1[maxn],h2[maxn],p1[maxn],p2[maxn];
ll geth1(ll l,ll r){
return h1[r]-h1[l-1]*p1[r-l+1];
}
ll geth2(ll l,ll r){
return (h2[r]-h2[l-1]*p2[r-l+1]%mod+mod)%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>(s+1);
n=strlen(s+1);
p1[0]=p2[0]=1;
for(int i=1;i<=n;i++){
h1[i]=h1[i-1]*p+s[i];
h2[i]=(h2[i-1]*p%mod+s[i])%mod;
p1[i]=p1[i-1]*p;
p2[i]=p2[i-1]*p%mod;
}
ll l1,r1,l2,r2;
cin>>l1>>r1>>l2>>r2;
if(geth1(l1,r1)==geth1(l1,r1)&&geth2(l1,r1)==geth2(l2,r2)){
cout<<"Yes";
}else{
cout<<"No";
}
return 0;
}
例题 洛谷P4591 碱基序列
题目大意
给定一个字符串\(S\)以及\(n\)组字符串\(A_n\),现在从每组字符串\(A_i\)中都随机选出一个字符串,按组号顺序首尾拼接成字符串\(B\),定义该情况的方案数为\(S\)中与\(B\)相等的子串总个数,求所有情况的方案数总和.
分析
可以提前算出\(S\)的哈希值,在后面每给出一个字符串\(A_i\)后,计算\(A_i\)的哈希值,与\(S\)的子串的哈希值进行匹配,匹配成功后用动态规划计算最终答案.
动态规划设计状态与状态转移
设\(dp[i][j]\)表示\(A_i\)选完后,完成\(S\)的前\(j\)个字符有几种可能性(由于没有说从必须到开头或结尾,所以\(dp[0][j]\)需要赋初值为\(1\),\(0\leq j\leq s.size()\)).
代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll p=13331,mod=1e9+7;
ll n,m,a,ans,hashx,hashs[10005],po[100005],dp[105][10005];
string s,x;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>s;
m=s.size();
for(ll i=0;i<=m;i++){
dp[0][i]=1;
}
po[0]=1;
for(ll i=1;i<=m;i++){
po[i]=po[i-1]*p;
hashs[i]=hashs[i-1]*p+s[i-1];
}
for(ll i=1;i<=n;i++){
cin>>a;
for(ll j=1;j<=a;j++){
cin>>x;
hashx=0;
for(ll k=1;k<=x.size();k++){
hashx=hashx*p+x[k-1];
}
for(ll l=1,r=x.size();r<=m;l++,r++){
if(hashs[r]-po[x.size()]*hashs[l-1]==hashx){
dp[i][r]=(dp[i-1][l-1]+dp[i][r])%mod;
}
}
}
}
for(ll i=1;i<=m;i++){
ans=(ans+dp[n][i])%mod;
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号