Distinct Substrings(spoj694)(sam(后缀自动机)||sa(后缀数组))

Given a string, we need to find the total number of its distinct substrings.

Input

\(T-\) number of test cases. \(T<=20\);
Each test case consists of one string, whose length is \(<=1000\)

Output

For each test case output one number saying the number of distinct substrings.

Sample Input

2
CCCCC
ABABA

Sample Output

5
9

题意:

给出\(n\)个串,求每个串中本质不同的子串

题解:

一、后缀自动机

把串前一个后缀自动机,然后在每次加入字符的时候把答案加上当前长度和他\(parent\)的点的长度的差。这里利用了后缀自动机的一个性质:

  • 每个点后面的本质不同的串的个数等于这个点的长度减去他的\(parent\)的长度。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
char s[N];
int a[N],c[N],as;
struct SAM{
    int last,cnt;
    int size[N],ch[N][52],fa[N<<1],l[N<<1];
    void ins(int c){
        int p=last,np=++cnt;last=np;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][c];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
        as+=l[np]-l[fa[np]];
    }
    void build(char s[]){
        memset(ch,0,sizeof ch);
        memset(l,0,sizeof l);
        memset(fa,0,sizeof fa);
        memset(size,0,sizeof size);
        int len=strlen(s+1);
        last=cnt=1;
        for(int i=1;i<=len;++i){
            if('A'<=s[i]&&s[i]<='Z')ins(s[i]-'A');
            else ins(s[i]-'a'+26);
        }
    }
}sam;
int main(){
    int n;
    cin>>n;
    while(n--){
    	as=0;
        scanf("%s",s+1);
        sam.build(s);
        printf("%d\n",as);
    }
}

二、后缀数组

处理出sa和height,以公式 当前后缀的贡献%c[i]=n-sa[i]+1-height[i]$计算出结果就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char s[N];
int n;
int fir[N],sec[N],rnk[N],t[N],sa[N],b[N];
void sort(){
    memset(t,0,sizeof t);
    for(int i=1;i<=n;++i)t[sec[i]]++;
    for(int i=1;i<N;++i)t[i]+=t[i-1];
    for(int i=n;i;--i)b[t[sec[i]]--]=i;
    memset(t,0,sizeof t);
    for(int i=1;i<=n;++i)t[fir[b[i]]]++;
    for(int i=1;i<N;++i)t[i]+=t[i-1];
    for(int i=n;i;--i)sa[t[fir[b[i]]]--]=b[i];
}
int height[N];
void get_height(char *s){
    int k=0;
    for(int i=1;i<=n;++i){
        if(rnk[i]==1){
            height[i]=0;
            continue;
        }
        if(k)--k;
        int j=sa[rnk[i]-1];
        while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
        height[i]=k;
    }
}
void get_sa(char *s){
    for(int i=1;i<=n;++i)rnk[i]=s[i];
    for(int k=1;k<=n;k*=2){
        for(int i=1;i<=n;++i){
            fir[i]=rnk[i];
            if(i+k>n)sec[i]=0;
            else sec[i]=rnk[i+k];
        }
        sort();
        int num=1;rnk[sa[1]]=1;
        for(int i=2;i<=n;++i){
            if(fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]])num++;
            rnk[sa[i]]=num;
        }
        if(num==n)break;
    }	
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%s",s+1);
        n=strlen(s+1);
        get_sa(s);
        get_height(s);
        long long ans=0;
        for(int i=1;i<=n;++i){
            ans+=n-sa[i]-height[i]+1;
        }
        printf("%lld\n",ans);
    }
}

posted @ 2018-12-11 19:11  整理者  阅读(168)  评论(0编辑  收藏  举报