【bzoj3796】Mushroom追妹子(二分+哈希)

本文介绍了一种使用哈希方法解决寻找最长公共子串的问题,该子串同时存在于两个给定字符串中,但不能包含第三个字符串。通过预处理字符串的哈希值,并结合二分查找和集合操作实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给定字符串s1,s2,s3s1,s2,s3s1,s2,s3,求一个字符串www,它同时是s1,s2s1,s2s1,s2的子串,且它的子串中不含有s3s3s3,求www的最大可能长度。
数据范围:1&lt;=∣s1∣,∣s2∣&lt;=50000,1&lt;=∣s3∣&lt;=100001&lt;=|s1|,|s2|&lt;=50000,1&lt;=|s3|&lt;=100001<=s1,s2<=50000,1<=s3<=10000

哈希大法好!
首先我们预处理出s1s1s1s2s2s2的前缀哈希值,s3s3s3的哈希值。
第一步是先处理出在s1s1s1s2s2s2中找到有哪些位置是s3s3s3。既然有哈希值了,为什么不直接用哈希来进行字符串匹配呢?(当然KMP也可以)我们枚举s1(s2)s1(s2)s1(s2)的每一位,然后判断以这个位置为开头,往后∣s3∣|s3|s3这一段子串的哈希值(这个可以利用前缀哈希值随便搞定)是否等于www的哈希值,然后就好了。
然后二分答案,用一个set做到O(nlog2n)O(nlog_2n)O(nlog2n)暴力判断。
如果有误在评论区吼一声哦!
具体的话请看代码:

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const unsigned long long p=133706993421;
char s1[50010],s2[50010],s3[10010];
int n,len1,len2,len3,cnt1,cnt2,end1[50010],end2[50010],ans;
unsigned long long hash1[50010],hash2[50010],hash3[10010],base[50010];
bool check(int len){
	set<unsigned long long>S;
	int t=0;
	for(int i=len;i<=len1;i++){
		while(end1[t+1]<=i&&t<cnt1)
			t++;
		if(!t||(i-len+1>end1[t]-len3+1))
			S.insert((hash1[i]-hash1[i-len])*base[n-i]);
	}
	t=0;
	for(int i=len;i<=len2;i++){
		while(end2[t+1]<=i&&t<cnt2)
			t++;
		if(!t||(i-len+1>end2[t]-len3+1))
			if(S.count((hash2[i]-hash2[i-len])*base[n-i]))
				return 1;
	}
	return 0;
}
int main(){
	scanf("%s%s%s",s1+1,s2+1,s3+1);
	len1=strlen(s1+1);
	len2=strlen(s2+1);
	len3=strlen(s3+1);
	n=max(len1,max(len2,len3));
	base[0]=1;
	for(int i=1;i<=n;i++)
		base[i]=base[i-1]*p;
	for(int i=1;i<=len1;i++)
		hash1[i]=hash1[i-1]+(s1[i]-'a'+1)*base[i];
	for(int i=1;i<=len2;i++)
		hash2[i]=hash2[i-1]+(s2[i]-'a'+1)*base[i];
	for(int i=1;i<=len3;i++)
		hash3[i]=hash3[i-1]+(s3[i]-'a'+1)*base[i];
	for(int i=len3;i<=len1;i++)
		if(hash1[i]-hash1[i-len3]==hash3[len3]*base[i-len3])
			end1[++cnt1]=i;
	for(int i=len3;i<=len2;i++)
		if(hash2[i]-hash2[i-len3]==hash3[len3]*base[i-len3])
			end2[++cnt2]=i;
	int l=0,r=min(len1,len2);
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值