hdu4622([u,v]内有多少个子串)

题:http://acm.hdu.edu.cn/showproblem.php?pid=4622

题意:求[u,v]区间内有多少不同的子串,N<=2000,q<=10000

分析:建立i....n个字符串的SAM,就可以预处理各个区间字符串的数目。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int M=2e3+3;
int trans[M<<1][26],slink[M<<1],maxlen[M<<1];
int endpos[M<<1];
int last,now,root;

void init(){
    now=last=root=1;
    memset(trans,0,sizeof(trans));
    memset(slink,0,sizeof(slink));
    memset(maxlen,0,sizeof(maxlen));
}
void extend(int c){
    maxlen[++now]=maxlen[last]+1;
    int p=last,np=now;
    while(p&&!trans[p][c]){
        trans[p][c]=np;
        p=slink[p];
    }
    if(!p)
        slink[np]=root;
    else{
        int q=trans[p][c];
        if(maxlen[p]+1!=maxlen[q]){
            maxlen[++now]=maxlen[p]+1;
            int nq=now;
            memcpy(trans[nq],trans[q],sizeof(trans[q]));
            slink[nq]=slink[q];
            slink[q]=slink[np]=nq;
            while(p&&trans[p][c]==q){
                trans[p][c]=nq;
                p=slink[p];
            }
        }
        else
            slink[np]=q;
    }
    last=np;
    endpos[np]=1;
}
char s[M];
ll ans[M][M];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",s);
        int n=strlen(s);
        for(int i=0;i<n;i++){
            init();
            ll sum=0;
            for(int j=i;j<n;j++){
                extend(s[j]-'a');
                sum+=maxlen[last]-maxlen[slink[last]];
                ans[i+1][j+1]=sum;
            }
        }
        int q;
        scanf("%d",&q);
        while(q--){
            int u,v;
            scanf("%d%d",&u,&v);
            printf("%lld\n",ans[u][v]);
        }
    }
    return 0;
}
View Code

 

posted @ 2020-07-21 14:53  starve_to_death  阅读(128)  评论(0编辑  收藏  举报