Wannafly挑战赛19-F-K串(hash+莫队)

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值