• 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
    ",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
    ",ans);
        }
    }
    
    
  • 相关阅读:
    LeetCode 204
    华为OJ2051-最小的K个数(Top K问题)
    华为OJ1964-求解立方根(牛顿迭代法)
    华为OJ2288-合唱队(最长递增子序列)
    华为OJ2011-最长公共子串
    【Unix编程】进程间通信(IPC)
    可利用空间表(Free List)
    13.10 Scrapy 通用爬虫
    13.9 Scrapy 对接 Splash
    第十四章 分布式爬虫
  • 原文地址:https://www.cnblogs.com/zhenglier/p/10104389.html
走看看 - 开发者的网上家园