字符串哈希

字符串哈希

  取一固定值\(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\)的哈希值为

\[HashT=(HashS-HashST\ast P^{sizeT})%Mod \]

  这就相当于通过\(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;
}
posted @ 2026-10-17 16:28  beiciqi  阅读(5)  评论(0)    收藏  举报