题目
题目链接: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;
}