Niuke K string hash + prefix sum + mo team

topic link

Title description:
ZZT got a string S and an integer K.
WZH proposed the definition of "elegant K string" in 1995: the number of each character in this string is a multiple of K.
Now ZZT wants to query the string for Q times, and the i-th query gives an interval [Li, Ri], and he wants to calculate how many substrings in [Li, Ri] are "elegant K strings".
Since ZZT is busy with work, he entrusts this problem to you, please help to solve it.

Input description:
Input a positive integer K in the first line.
The second line inputs a string S.
The third line inputs a positive integer Q, indicating that there are Q queries.
In the next Q lines, input two positive integers Li and Ri in each line, representing the ith query.
1 ≤ K ≤ 50.
1≤ | S | ≤ 3 x 104 and S contains only lowercase English letters.
1≤ Q ≤ 3 x 104.
1 ≤ Xi ≤ Yi ≤ N.
Output description:
output a positive integer for each query , indicating the number of "elegant K strings" that satisfy the condition.
Example 1

input
1
abc
3
1 3
1 2
2 3

output
6
3
3

Analysis:
I didn't know how to query at first, but after reading the solution, I realized that there is another algorithm called Mo Team (%%%).
The core of Team Mo’s algorithm is as follows:

 int l = 1 , r = 0;
    for(int i = 1; i <= m; i ++) {
    
    
        while(l < ask[i].left) del(l ++);
        while(l > ask[i].left) add(-- l);
        while(r < ask[i].right) add(++ r);
        while(r > ask[i].right) del(r --);
        Ans[ask[i].id] = ans;
    }

For different topics, you need to write the del and add functions yourself. I won’t go into details about Team Mo’s algorithm here, but mainly talk about how to implement it in this topic.

Because this question requires how many substrings in the interval are in line with the meaning of the question, by constructing the prefix sum, the answer can be obtained in the time complexity of O(1), which needs to be thought of.

#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <cstring>
#include <stack>
#include <string>
using namespace std;

typedef long long ll;
const int N = 3e4+199;
const double Pi = acos(-1);
char str[N];
int blo;
ll a[N],pre_num[N][30],ans[N],base=31,sum;
map<ll,int>ma;
struct node{
    
    
    int l,r,id;
    bool operator < (const node & a) const{
    
    
        return l/blo==a.l/blo? r<a.r : l/blo<a.l/blo;
    }
}num[N];
void Add(int x){
    
    
    sum+=ma[x]++;//通过观察满足题意的序列可以得到一个递推关系,即1-->3-->6-->10
                //然而这个关系正好是1+2+3+4+……
}
void Minus(int x){
    
    
    sum-=--ma[x];
}
int main()
{
    
    

    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    int Q,k;
    scanf("%d\n",&k);
    scanf("%s%d",str+1,&Q);
    int len = strlen(str+1);
    blo = sqrt(len+1);

    for(int i=1;i<=len;i++){
    
    
        for(int j=0;j<26;j++){
    
    
            pre_num[i][j]=pre_num[i-1][j];
        }
        pre_num[i][str[i]-'a']=(pre_num[i][str[i]-'a']+1)%k;
        //这里%k的原因是为了说明当pre_num[i][j]=0的时候,是满足题意
        //等于0相当于是一个起点,更好的体现是在莫队算法的初始状态
        //如果不模k,我们就不知道满足题意的初始状态是多少,也就无法在Add或Minus两个函数中迭代答案了;
    }

    for(int i=1;i<=len;i++){
    
    
        ll pp = 0;
        for(int j=0;j<26;j++){
    
    
            pp = pp*base + pre_num[i][j];
        }
        a[i]=pp;//我们将每个位置的26个英文字母看成一个点,这样就可以实现哈希记录
    }

    for(int i=0;i<Q;i++){
    
    
        scanf("%d%d",&num[i].l,&num[i].r);
        num[i].l--;//这里之所以减一,就需要结合前缀和来理解了,要求[1,3]的答案,那就需要用b[3]-b[0]
        num[i].id=i;
    }
    sort(num,num+Q);
    int l=0,r=-1;
    for(int i=0;i<Q;i++){
    
    
        while(r<num[i].r) Add(a[++r]);
        while(r>num[i].r) Minus(a[r--]);
        while(l>num[i].l) Add(a[--l]);
        while(l<num[i].l) Minus(a[l++]);
        ans[num[i].id]=sum;
    }
    for(int i=0;i<Q;i++){
    
    
        printf("%lld\n",ans[i]);
    }
    return 0;
}

Guess you like

Origin blog.csdn.net/c___c18/article/details/115522561