Hash数组 快速进行字符串匹配

转自https://blog.csdn.net/Mikchy/article/details/103995537

例题https://leetcode-cn.com/problems/distinct-echo-substrings/submissions/

 1.自然溢出法

对于自然溢出方法,我们定义 Base ,而MOD对于自然溢出方法,就是 unsigned long long 整数的自然溢出,相当于MOD 是2^6^4-1

 hash[i]=hash[i-1]*Base+idx(s[i])

#include<iostream>//自然溢出哈希 求解对称子串问题 17行展示遍历子串的技巧 
#include<set>//题目https://leetcode-cn.com/problems/distinct-echo-substrings/
#include<string>//解析https://blog.csdn.net/Mikchy/article/details/103995537 
#define ull unsigned long long
using namespace std;
const int N=1e6+10;
ull base=29;//基数要选素数 
ull hash[N],p[N];//哈希数组,基数幂数组 
set<ull> h;//存放答案 
int solution(string text){
	int n=text.size();
	hash[0]=0,p[0]=1;
	for(int i=0;i<n;i++)
		hash[i+1]=hash[i]*base+(text[i]-'a'+1);
	for(int i=1;i<n;i++)
		p[i]=p[i-1]*base;
	for(int len=2;len<=n;len+=2){//遍历长度为len的子串是否对称 
		for(int i=0;len+i-1<n;i++){//从字符串开头遍历到结尾  字符串为[i,i+len-1]  计算方法:x-i+1=len 
			int l1=i,r1=i+(len>>1)-1;//左半部分为[i,len/2+i-1] 计算方法:x-i+1=len/2 
			int l2=i+(len>>1),r2=i+len-1;//右半部分为[len/2+i,i+len-1] 
			ull left=hash[r1+1]-hash[l1]*p[r1-l1+1];
			ull right=hash[r2+1]-hash[l2]*p[r2-l2+1];
			if(left==right)	h.insert(left);
		}
	}		
	return h.size();
}
int main(){
	string text;
	cin>>text;
	cout<<solution(text);
	return 0;
}

 2.单Hash

定义了 Base 和 MOD,有了对应的要求余 MOD。所以一般用 long long 就可以了。

hash[i]=(hash[i-1]*Base+idx(S[i]))%MOD

#include<iostream>//单哈希 求解对称子串问题 
#include<set>//题目https://leetcode-cn.com/problems/distinct-echo-substrings/
#include<string>//解析https://blog.csdn.net/Mikchy/article/details/103995537 
#define ll long long
using namespace std;
const int N=1e6+10;const ll MOD = 1e9 + 7;
ll base=29;//基数要选素数
ll hash[N],p[N];//哈希数组,基数幂数组
set<ll> h;//存放答案
int solution(string text) {
	int n=text.size();
	hash[0]=0,p[0]=1;
	for(int i=0; i<n; i++)
		hash[i+1]=(hash[i]*base+(text[i]-'a'+1))% MOD;
	for(int i=1; i<n; i++)
		p[i]=(p[i-1]*base)% MOD;
	for(int len=2; len<=n; len+=2) { //遍历长度为len的子串是否对称
		for(int i=0; len+i-1<n; i++) { //从字符串开头遍历到结尾  字符串为[i,i+len-1]  计算方法:x-i+1=len
			int l1=i,r1=i+(len>>1)-1;//左半部分为[i,len/2+i-1] 计算方法:x-i+1=len/2
			int l2=i+(len>>1),r2=i+len-1;//右半部分为[len/2+i,i+len-1]
			ll left = ((hash[r1 + 1] - hash[l1] * p[r1 + 1 - l1]) % MOD + MOD) % MOD;
			ll right = ((hash[r2 + 1] - hash[l2] * p[r2 + 1 - l2]) % MOD + MOD) % MOD;
			if(left==right)	h.insert(left);
		}
	}
	return h.size();
}
int main() {
	string text;
	cin>>text;
	cout<<solution(text);
	return 0;
}

3.双Hash

将一个字符串用不同的Base和MOD,hash两次,将这两个结果用一个二元组表示,作为一个总的Hash结果。
hash1[i]=(hash1[i-1]*Base1+idx*(s[i]))%MOD1

hash2[i]=(hash2[i-1]*Base2+idx*(s[i]))%MOD2

 这个方法可以很有效的避免出现冲突。 结果再用pair来存就可以了,之后比较两个二元组是否相同。

<hash1[i],hash2[i]>

#include<iostream>//双哈希 求解对称子串问题 
#include<set>//题目https://leetcode-cn.com/problems/distinct-echo-substrings/
#include<string>//解析https://blog.csdn.net/Mikchy/article/details/103995537 
#include<map> 
#define ll long long
using namespace std;
const int N=1e6+10;const ll MOD1 = 1e9 + 7,MOD2 = 1e9 + 9;
ll base1=29,base2=131;//基数要选素数
ll hash1[N],p1[N],hash2[N],p2[N];//哈希数组,基数幂数组
set<pair<ll,ll> > h; // 因为是一个二元组,所以可以用 pair 容器。
int solution(string text) {
	int n=text.size();
	hash1[0]=0,p1[0]=1,hash2[0]=0,p2[0]=1;
	for(int i=0; i<n; i++){
		hash1[i+1]=(hash1[i]*base1+(text[i]-'a'+1))% MOD1;	
		hash2[i+1]=(hash2[i]*base2+(text[i]-'a'+1))% MOD2;
	}
	for(int i=1; i<n; i++){
		p1[i]=(p1[i-1]*base1)% MOD1;
		p2[i]=(p2[i-1]*base2)% MOD2;
	}
	for(int len=2; len<=n; len+=2) { //遍历长度为len的子串是否对称
		for(int i=0; len+i-1<n; i++) { //从字符串开头遍历到结尾  字符串为[i,i+len-1]  计算方法:x-i+1=len
			int l1=i,r1=i+(len>>1)-1;//左半部分为[i,len/2+i-1] 计算方法:x-i+1=len/2
			int l2=i+(len>>1),r2=i+len-1;//右半部分为[len/2+i,i+len-1]
			ll left1 = ((hash1[r1 + 1] - hash1[l1] * p1[r1 + 1 - l1]) % MOD1 + MOD1) % MOD1;
			ll left2 = ((hash2[r1 + 1] - hash2[l1] * p2[r1 + 1 - l1]) % MOD2 + MOD2) % MOD2;
			ll right1 = ((hash1[r2 + 1] - hash1[l2] * p1[r2 + 1 - l2]) % MOD1 + MOD1) % MOD1;
			ll right2 = ((hash2[r2 + 1] - hash2[l2] * p2[r2 + 1 - l2]) % MOD2 + MOD2) % MOD2;
			if(left1 == right1 && left2 == right2) h.insert(make_pair(left1, left2));
		}
	}
	return h.size();
}
int main() {
	string text;
	cin>>text;
	cout<<solution(text);
	return 0;
}

求子串的Hash值

 

直接看可知S的[1,2]的子串就等于S1,所以它们对应的Hash应该相同。 所以要将之前的所有系数都消掉。

记得结果要取模运算

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值