zoukankan      html  css  js  c++  java
  • SAM:后缀自动机

    好文转载

    luoguP3804

    代码:

    /*
    定义.对给定字符串s的后缀自动机是一个最小化确定有限状态自动机,它能够接收字符串s的所有后缀。
        对给定字符串s的后缀自动机是一个最小化确定有限状态自动机,它能够接收字符串s的所有后缀。
        某一状态t_0被称作初始状态,由它能够到达其余所有状态。
        自动机中的所有转移——即有向边——都被某种符号标记。从某一状态出发的诸转移必须拥有不同的标记。(另一方面,状态转移不能在任何字符上)。
        一个或多个状态被标记为终止状态。如果我们从初始状态t_0经由任意路径走到某一终止状态,并顺序写出所有经过边的标记,你得到的字符串必然是s的某一后缀。
        在符合上述诸条件的所有自动机中,后缀自动机有这最少的顶点数。(后缀自动机并不被要求拥有最少的边数)
        
    最简性:它包含了所有s的子串的信息。换言之,对于任意从初始状态t_0出发的路径,如果我们写出所经过边上的标记,形成的子串必须是s的子串。相应地,s的任意子串都对应一条从初始状态t_0出发的路径。
    为了简化说明,我们称子串“匹配”了从初始状态出发的路径,如果该路径上的边标记组成了这一子串。相应地,我们称任意路径“匹配”某一子串,该子串由路径中边的标记组成。
    后缀自动机的每个状态都引领一条或多条从初始状态出发的路径。我们称这个状态有若干匹配这些路径的方法。
    
    引理1.两个非空子串u和v(length(u)<=length(v))是终点等价的,当且仅当u在字符串s中仅作为w的后缀出现。
    引理2.考虑两个非空子集u,w(length(u)<=length(w))。它们的终点集合不相交,或者endpos(w)是endpos(u)的子集。进一步地,这取决于u是否是w的后缀:
    引理3.考虑一个终点等价类。将该等价类中的子串按长度递减排序。排序后的序列中,每个子串将比上一个子串短,从而是上一个字串的后缀。换句话说,某一终点等价类中的字符串互为后缀,它们的长度依次取区间[x,y]内的所有数。
    引理4.后缀链接组成了一棵以t_0为根的树。
    引理5.如果我们将所有合法的终点集合建成一棵树(使得孩子是父母的子集),这棵树将和后缀链接构成的树相同。
    
    状态的数量:
        由长度为n的字符串s建立的后缀自动机的状态个数不超过2n-1(对于n>=3)。
    转移的数量:
        由长度为n的字符串s建立的后缀自动机中,转移的数量不超过3n-4(对于n>=3)。
    
    定理1.DAWG(s)中后缀链接组成的树就是后缀树ST(rev(s))。
    定理2.图DAWG(s)的边都能用后缀树ST(rev(s))的扩展指针表示。另外,DAWG(s)中的连续转移就是ST(rev(s))中反向的后缀指针。
    定理3.使用后缀自动机DAWG(s),我们可以用O(n)的时间构建后缀树ST(rev(s))。
    定理4.使用后缀树ST(rev(s)),我们可以用O(n)的时间构建后缀自动机DAWG(s)。
    
    */
    #include <cstdio>
    #include <complex>
    #include <cstring>
    #ifdef Win32
    #define LL "%I64d"
    #else
    #define LL "%lld"
    #endif
    const int N=2e6+5;
    typedef long long ll;
    char s[N];
    int a[N],c[N],size[N],n;
    ll ans=0;
    inline int max(int x,int y)
    {return x>y?x:y;}
    struct SuffixAutoMaton
    {
        int last,cnt,ch[N<<1][26],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;
                }
            }
            size[np]=1;
        }
        void build()
        {
            scanf("%s",s+1);
            int len=strlen(s+1);
            last=cnt=1;
            for(int i=1;i<=len;++i)
                ins(s[i]-'a');
        }
        void calc()
        {
            for(int i=1;i<=cnt;++i)++c[l[i]];
            for(int i=1;i<=cnt;++i)c[i]+=c[i-1];
            for(int i=1;i<=cnt;++i)a[c[l[i]]--]=i;
            for(int i=cnt;i;--i)
            {
                int p=a[i];
                size[fa[p]]+=size[p];
                if(size[p]>1)
                    ans=max(ans,1LL*size[p]*l[p]);
            }
            printf(LL "
    ",ans);
        }
    }sam;
    int main()
    {
        sam.build();
        sam.calc();
        return 0;
    }
    View Code
  • 相关阅读:
    python深浅拷贝
    pyinstaller打包py文件为exe方法
    python学习笔记3-关于文件的复制、重命名、移动、删除操作
    BeautifulSoup4 print() 输出中文乱码解决方法
    进程和线程的开启效率
    python3 使用pymysql
    python3 __file__
    Flask Template ( 模板学习)
    响应对象
    nginx 以及 uwsgi 的配置
  • 原文地址:https://www.cnblogs.com/kuaileyongheng/p/8665945.html
Copyright © 2011-2022 走看看