相似串匹配(哈希)

1、题面:

2、题解:

现将字符串转化为一个径B函数处理过的字符串,bs,bt,

(B函数:如果当前字符i前面有和它相同的字符j,B(i) = i-j,否组B(i) = len,

这样处理通过记录字符串的前驱,使字符串所有相同字符和不同字符的相对位置都确定下来,相当于一个链表,

所以如果字符串Q,P是相似子串,则B(Q) = B(P)。)

然后遍历求出bs串的长度为m的子串,判断是否有字符串bs’与bt串的哈希值相同。

注意,这里可以寻找S[i,j]与S[i+1,j+1]之间的关系,如果前进了一位,质询要考虑被删除的字符S[i]与

它后面第一个与S【i】相同的字符s[j](就是S[i]的后继),的关系,只有j的相对位置发生变化,所以修改B(j)的值即可。

(感谢阜神学长的代码)

 

官方题解:

 

3、代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const ull base = 133;
const int maxn = 1e6+10;
int s[maxn],bs[maxn],t[maxn],bt[maxn],pre[maxn],nxt[maxn],n,m;
struct Hash{
	int hs[maxn],p[maxn];
	void Insert(int a[],int len){
		p[0] = 1;hs[0] = 0;
		for(int i=1;i<=len;i++){
			p[i] = p[i-1]*base;
			hs[i] = hs[i-1]*base+a[i];
		}
	}
	ull Get_Hash(int l,int r){
		return hs[r]-hs[l-1]*p[r-l+1];
	}
}S,T;
int main(void){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&s[i]);
		if(pre[s[i]]==0){
			bs[i] = n;//转化为B数组 
		}
		else bs[i] = i-pre[s[i]];
		pre[s[i]] = i;
	}
	memset(pre,0,sizeof(pre));
	for(int i=1;i<=m;i++){
		scanf("%d",&t[i]);
		if(pre[t[i]]==0){
			bt[i] = n;//转化为B数组 
		}
		else bt[i] = i-pre[t[i]];
		pre[t[i]] = i;
	}
	for(int i=1;i<maxn;i++) pre[i] = n+1;//记录前驱 
	for(int i=n;i>=1;i--){
		nxt[i] = pre[s[i]]; //记录后缀 
		pre[s[i]] = i;
	}
	S.Insert(bs,n);
	T.Insert(bt,m);
	int ans = 0;
	ull ans1 = T.Get_Hash(1,m),ans2 = S.Get_Hash(1,m);
	if(ans1==ans2) ans++;//比较哈希值 
	for(int l=2,r=m+1;r<=n;l++,r++){
		ans2 = S.Get_Hash(l,r);
		int bor = nxt[l-1];
		if(bor<=r){
			ans2 = ans2+(n-bs[bor])*S.p[(r-l+1)-(bor-l+1)];//修改后面的哈希值 
		}
		if(ans2==ans1) ans++;
	}
	printf("%d\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值