[HDU4622]Reincarnation 后缀自动机

题意

求给定字符串的[l,r]区间内不同的子串个数。


注意到n只有2000,对每个后缀建后缀自动机,边往后缀自动机里插入字符,边记录子串个数,对于询问O(1)输出。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#define N 2010

using namespace std;

int n,t,m,Ans[N][N];
char A[N];

inline char C(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2)return EOF;
    }
    return *p1++;
}

inline void reaD(int &x){
    char Ch=C();x=0;
    for(;Ch>'9'||Ch<'0';Ch=C());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=C());
}

inline void reaD(char *x){
    char Ch=C();int len=0;
    for(;Ch>'z'||Ch<'a';Ch=C());
    for(;Ch>='a'&&Ch<='z';x[len++]=Ch,Ch=C());x[len]=0;
}

struct SAM_{
    int next[N<<3][30],fail[N<<3],stp[N<<3],p,cnt,tot;
    SAM_(){p=cnt=1;}
    int add(int x){
        int np=++cnt;stp[np]=stp[p]+1;
        memset(next[np],0,sizeof(next[np]));
        while(p&&!next[p][x]) next[p][x]=np,p=fail[p];
        if(!p) fail[np]=1;
        else{
            int q=next[p][x];
            if(stp[q]==stp[p]+1) fail[np]=q;
            else{
                int nq=++cnt;stp[nq]=stp[p]+1;
                memset(next[nq],0,sizeof(next[nq]));
                memcpy(next[nq],next[q],sizeof(next[q]));
                fail[nq]=fail[q];
                fail[np]=fail[q]=nq;
                while(next[p][x]==q) next[p][x]=nq,p=fail[p];
            }
        }
        p=np;
        return tot+=stp[np]-stp[fail[np]];
    }
    void clear(){
        cnt=p=1;tot=0;
        memset(next[p],0,sizeof(next[p]));
        //memset(next,0,sizeof(next));
        //memset(fail,0,sizeof(fail));
        //memset(stp,0,sizeof(stp));
    }
}SAM;

int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    for(reaD(t);t;t--){
        reaD(A+1);n=strlen(A+1);
        for(int i=1;i<=n;i++){
            SAM.clear();
            for(int j=i;j<=n;j++)
                Ans[i][j]=SAM.add(A[j]-'a');
        }
        reaD(m);
        for(int i=1,a,b;i<=m;i++){
            reaD(a);reaD(b);
            printf("%d\n",Ans[a][b]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值