计蒜客:String and Times(后缀自动机)

Now you have a string consists of uppercase letters, two integers AA and BB. We call a substring wonderful substring when the times it appears in that string is between AA and BB (A \le times \le BA≤times≤B). Can you calculate the number of wonderful substrings in that string?

Input

Input has multiple test cases.

For each line, there is a string SS, two integers AA and BB.

\sum length(S) \le 2 \times 10^6∑length(S)≤2×106,

1 \le A \le B \le length(S)1≤A≤B≤length(S)

Output

For each test case, print the number of the wonderful substrings in a line.

样例输入复制

AAA 2 3
ABAB 2 2

样例输出复制

2
3

题目来源

ACM-ICPC 2018 焦作赛区网络预赛

题意:计算出现次数为A到B的子串个数。

思路:构造后缀自动机,拓扑排序算出每个集合的出现次数,然后用当前MAX减一下父亲集合的MAX就能算出每个集合的子串数量了。

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 2e5+30;
char s[maxn];
int g[maxn][26], pre[maxn], mx[maxn];
int b[maxn], f[maxn], id[maxn];
int sz, last;
void newnode(int s){
    mx[++sz] = s;
    pre[sz] = 0;
    memset(g[sz], 0, sizeof(g[sz]));
}
void init(){
    sz = 0;
    last = 1;
    newnode(0);
}
int ins(int x){
    newnode(mx[last] + 1);
    int p = last, np = sz;
    while(p && !g[p][x]){
        g[p][x] = np;
        p = pre[p];
    }
    if(p){//边发生冲突
        int q = g[p][x];
        if(mx[q] == mx[p] + 1) pre[np] = q;//新增结束点,不改变原图
        else{
            newnode(mx[p] + 1);//新增点
            int nq = sz;
            for(int j=0; j<26; ++j) g[nq][j] = g[q][j];
            pre[nq] = pre[q];
            pre[q] = pre[np] = nq;
            while(p && g[p][x] == q){
                g[p][x] = nq;
                p = pre[p];
            }
        }
    }
    else
        pre[np] = 1;
    last = np;
}
int main(){
    int L, R;
    while(~scanf("%s%d%d",s+1,&L,&R)){
        init();
        memset(b, 0, sizeof(b));
        memset(f, 0, sizeof(f));
        memset(id, 0, sizeof(id));
        int len = strlen(s+1);
        for(int i=1; i<=len; ++i) ins(s[i]-'A');
        for(int i=1,p=1; i<=len; ++i)
            p = g[p][s[i]-'A'],++f[p];//主链加上1,表示至少出现1次
        for(int i=1; i<=sz; ++i) ++b[mx[i]];//基数排序
        for(int i=1; i<=len; ++i) b[i] += b[i-1];
        for(int i=1; i<=sz; ++i) id[b[mx[i]]--]=i;//排在第i的是哪个集合
        for(int i=sz; i; --i) f[pre[id[i]]] += f[id[i]];//按拓扑序累加
        LL ans = 0;
        for(int i=2; i<=sz; ++i)
            if(f[id[i]] >= L && f[id[i]] <= R)
                ans += mx[id[i]]-mx[pre[id[i]]];
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值