1. 程式人生 > >Wannafly挑戰賽19-F-K串(hash+莫隊)

Wannafly挑戰賽19-F-K串(hash+莫隊)

題目描述

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;
}