【YbtOJ#20072】相似子串

题目

题目链接:http://noip.ybtoj.com.cn/problem/20072

思路

对于一个长度为 (m) 的询问串,显然要求的就是 (s) 中有多少个长度为 (m) 的区间和等于询问串的和。
考虑根号分治。假设所有询问串串长和为 (t)

  • (mleq sqrt{t}) 时,我们可以预处理 (cnt[i][j]) 表示 (s) 长度为 (i) 的区间有多少个和为 (j)。然后每次可以 (O(1)) 回答。
  • (m>sqrt{t}) 时,显然这种询问次数不会超过 (frac{t}{sqrt t}=sqrt{t}) 次,所以我们每次 (O(n)) 暴力做即可。

时间复杂度 (O((n+m)sqrt{t}))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=200010,M=450;
int Q,T,m,n,ans,sum,num,cnt[M][M];
char s[N],t[N];

int main()
{
	freopen("similar.in","r",stdin);
	freopen("similar.out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1); T=sqrt(n);
	s[0]=48;
	for (int i=1;i<=T;i++)
	{
		sum=0;
		for (int j=1;j<i;j++) sum+=s[j]-48;
		for (int j=i;j<=n;j++)
		{
			sum=sum+(s[j]-48)-(s[j-i]-48);
			cnt[i][sum]++;
		}
	}
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%s",t+1);
		m=strlen(t+1); num=sum=0;
		for (int i=1;i<=m;i++) num+=t[i]-48;
		if (m<=T) printf("%d
",cnt[m][num]);
		else
		{
			ans=0;
			for (int i=1;i<m;i++) sum+=s[i]-48;
			for (int i=m;i<=n;i++)
			{
				sum=sum+(s[i]-48)-(s[i-m]-48);
				if (sum==num) ans++;
			}
			printf("%d
",ans);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/13879716.html