题目描述:
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
abc
3
1 3
1 2
2 3
输出:
6
3
3
分析
我们可以先求出前缀和,并对k取模,这样对于每一个位置,我们就可以把其看作是一个26元组,而每次的询问就相当于询问区间[L,R]中有多少对相同的26元组,然后将前缀和转哈希,我们就可以利用莫队算法来求解了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e4 + 10;
int sum, f[maxn], ans[maxn];
int p[maxn][26];
unsigned long long a[maxn];
char s[maxn];
int k, m, l, r;
map<unsigned long long, int> mp;
struct node{
int l, r, id;
bool operator < (const node &x) const{
return f[l] == f[x.l] ? r < x.r : l < x.l;
}
}q[maxn];
int main()
{
scanf("%d %s", &k, s + 1);
int len = strlen(s + 1);
for(int i = 1; s[i] != '\0'; i++){
for(int j = 0; j < 26; j++){
p[i][j] = p[i - 1][j];
}
p[i][s[i] - 'a'] = (p[i][s[i] - 'a'] + 1) % k;
}
for(int i = 1; s[i] != '\0'; i++){
unsigned long long pp = 0;
for(int j = 0; j < 26; j++){
pp = pp * 131 + p[i][j];
}
a[i] = pp;
}
int unit = sqrt(len);
for(int i = 1; i <= len; i++){
f[i] = i / unit + 1;
}
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
q[i].l--;
}
sort(q + 1, q + m + 1);
sum = 0;
l = q[1].l, r = q[1].l - 1;
for(int i = 1; i <= m; i++){
while(l < q[i].l){
sum -= --mp[a[l]];
l++;
}
while(l > q[i].l){
l--;
sum += mp[a[l]]++;
}
while(r < q[i].r){
r++;
sum += mp[a[r]]++;
}
while(r > q[i].r){
sum -= --mp[a[r]];
r--;
}
ans[q[i].id] = sum;
}
for(int i = 1; i <= m; i++){
printf("%d\n", ans[i]);
}
return 0;
}