链接:https://www.nowcoder.com/acm/contest/131/F
来源:牛客网
题目描述
ZZT 得到了一个字符串 S 以及一个整数 K。
WZH 在 1995 年提出了“优雅 K 串”的定义:这个字符串每一种字符的个数都是 K 的倍数。
现在 ZZT 想要对字符串进行 Q 次询问,第 i 次询问给出一个区间 [Li, Ri],他想计算 [Li, Ri] 中有多少个子串是“优雅 K 串”。
由于 ZZT 忙于工作,所以他把这个问题交给了你,请你帮忙解决。
输入描述:
第一行输入一个正整数 K。 第二行输入一个字符串 S。 第三行输入一个正整数 Q,表示有 Q 次询问。
接下来 Q 行,每行输入两个正整数 Li 和 Ri,表示第 i 次询问。
1 ≤ K ≤ 50.
1≤ | S | ≤ 3 x 104 且 S 仅包含小写英文字母.
1≤ Q ≤ 3 x 104.
1 ≤ Xi ≤ Yi ≤ N.
输出描述:
每次询问,输出一个正整数,表示满足条件的“优雅 K 串”的数量。
示例1
输入
复制
1 abc 3 1 3 1 2 2 3
输出
复制
6 3 3
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define maxn 100005
#define mod 1000000009
#define ll long long
char s[maxn];
int k,q,pos[maxn],sum[maxn];
int cnt,l,r,num,sm[maxn];
ll tmp,ans[maxn],c[maxn],v[maxn];
struct node
{
int l,r,id;
}a[maxn];
bool comp(node a,node b)
{
if(pos[a.l]!=pos[b.l])
return pos[a.l]<pos[b.l];
return a.r<b.r;
}
int main(void)
{
scanf("%d",&k);
scanf("%s",s+1);
scanf("%d",&q);
for(int i=1;i<=q;i++)
scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;
int len=strlen(s+1);
int relen=(int)sqrt(len+0.5);
for(int i=1;i<=q;i++)
pos[i]=(i-1)/relen+1;
sort(a+1,a+q+1,comp);
v[++cnt]=0;
for(int i=1;i<=q;i++)
{
sum[s[i]-'a']=(sum[s[i]-'a']+1)%k;
for(int j=0;j<26;j++)
c[i]=(c[i]*131+sum[j])%mod;
v[++cnt]=c[i];
}
sort(v+1,v+cnt+1);
num=unique(v+1,v+cnt+1)-v-1;
for(int i=0;i<=q;i++)
c[i]=lower_bound(v+1,v+cnt+1,c[i])-v;
sm[1]=1;
for(int i=1;i<=q;i++)
{
while(r<a[i].r)
r++,tmp+=sm[c[r]],sm[c[r]]++;
while(r>a[i].r)
sm[c[r]]--,tmp-=sm[c[r]],r--;
while(l+1<a[i].l)//因为前缀和左开右闭
sm[c[l]]--,tmp-=sm[c[l]],l++;
while(l+1>a[i].l)
l--,tmp+=sm[c[l]],sm[c[l]]++;
ans[a[i].id]=tmp;
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}