2021牛客寒假第四场 武辰延的字符串(二分+哈希)

题目链接:点击这里

题目大意:
给出两个字符串 s s s t t t ,求有多少对 s s s 的非空前缀连起来是 t t t 的前缀

题目分析:
我们可以先枚举 t t t 的每一个位置
如果 s [ i ] ≠ t [ i ] s[i]\not=t[i] s[i]=t[i] 显然用 s s s 的前 i i i 个字符不可能凑成 t t t 的一个子串(因为 s [ i ] s[i] s[i] t [ i ] t[i] t[i] 不同)
如果 s [ i ] = t [ i ] s[i]=t[i] s[i]=t[i] 我们可以二分来找用 s . s u b s t r ( 1 , i ) s.substr(1,i) s.substr(1,i) 和其子串能构成 t t t 的子串的方案数,具体方法如下:
i i i s s s t t t 相同,之后再用哈希判断 s . s u b s t r ( 1 , i ) s.substr(1,i) s.substr(1,i) 的子串是否可以继续匹配,此题我使用了双哈希

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#define ll long long
#define ull unsigned ll
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
//#define int ll
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
const int base = 131;
const int base2 = 233;
int n,m,a[maxn<<2];
char s[maxn],t[maxn];
ull sum1[maxn],sum2[maxn],pre1[maxn],pre2[maxn],p[maxn],p2[maxn];
pair<ull,ull> get_hash(int l,int r,ull a[],ull b[])
{
	return make_pair(a[r]-a[l-1]*p[r-l+1],(b[r]-b[l-1]*p2[r-l+1]%mod+mod)%mod);
}
signed main()
{
	scanf("%s%s",s+1,t+1);
	int len1 = strlen(s+1),len2 = strlen(t+1);
	p[0] = 1,p2[0] = 1;
	for(int i = 1;i <= max(len1,len2);i++) p[i] = p[i-1]*base,p2[i] = p2[i-1]*base2%mod;
	for(int i = 1;i <= len1;i++) sum1[i] = sum1[i-1]*base+s[i],pre1[i] = (pre1[i-1]*base2+s[i])%mod;
	for(int i = 1;i <= len2;i++) sum2[i] = sum2[i-1]*base+t[i],pre2[i] = (pre2[i-1]*base2+t[i])%mod;
	ll ans = 0;
	for(int i = 1;i <= min(len1,len2-1);i++)
	{
		if(s[i] != t[i]) break;
		int l = 0,r = len2-i,res = 0;
		while(r-l >= 0)
		{
			int mid = l+r>>1;
			if(get_hash(1,mid,sum1,pre1) == get_hash(i+1,i+mid,sum2,pre2)) l = mid+1,res = mid;
			else r = mid-1;	 
		}
		ans += res;
	}
	printf("%lld\n",ans);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值