• 一篇并不对劲的后缀自动机教程


    说是教程其实也就我自己看怕我这个shabi又双叒叕忘了后缀自动机然后再学一遍

    1.后缀自动机就是能识别字符串S所有后缀的自动机

    根据定义知道 它也可以识别S的所有子串

    2.Right集合

    是指子串str在母串S中出现的结束位置的集合

    对于一个Right集合,适合它的子串长度取值区间为$[minlen(s),maxlen(s)]$

    3.parent树

    根据上面Right集合的定义,我们可以按Right集合的包含关系建一棵树,就是parent树

    父亲的Right集合包含所有儿子的Right集合

    很容易知道parent树从上到下子串长度边长,Right集合变小

    我们令fa = parent(s)则可发现

    $Right(s)⊂Right(fa)$且$Right(fa)$最小

    发现$maxlen(fa)=minlen(s)1$

    parent树可以相当于fail树,一个单词匹配不上的时候可以沿着parent树往上跳

    4.maxlen

    根据Right集合的定义,一个点的maxlen其实就是转移图上root到x的最长路径长度

    顺便,minlen(x)是最短的

    5.其他可用的性质

    I.把maxlen基数排序,就得到了转移图的拓扑序

    II.在SAM上两个串的最长公共子串就是这两个点的LCA

    III.由于SAM的存在,我们可以把序列的东西强行上树,然后把树的东西强行上字符串,所以如果您做到SAM+树链剖分/SAM+LCT的时候,不要过早骂人

    struct SAM
    {
    int tr[maxn][26],fa[maxn],len[maxn],size[maxn];
        int ST[maxn][21],id[maxn],Cnt[maxn];
        int last,p,np,q,nq,cnt,rt;
        SAM(){last = ++cnt; rt = 1;}
        inline void extend(int c)
        {
            p = last, np = last = ++cnt, len[np] = len[p] + 1;size[np] = 1;
            while(!tr[p][c] && p) tr[p][c] = np, p = fa[p];
            if(!p) fa[np] = rt;
            else
            {
                q = tr[p][c];
                if(len[q] == len[p] + 1) fa[np] = q;
                else
                {
                    nq = ++cnt; len[nq] = len[p] + 1; memcpy(tr[nq],tr[q],sizeof(tr[q]));
                    fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                    while(tr[p][c] == q) tr[p][c] = nq,p = fa[p];
                }
            }
        }
        inline void buildsize()
        { 
            for(int i=1;i<=cnt;i++)Cnt[len[i]]++;
            for(int i=1;i<=n;i++)Cnt[i] += Cnt[i-1];
            for(int i=1;i<=cnt;i++)id[Cnt[len[i]]--] = i;
            for(int i=cnt;i>=1;i--)size[fa[id[i]]] += size[id[i]];
        }
    }sam;
    代码
  • 相关阅读:
    VS编译错误:#error: Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version
    C++ STL std::copy 详解
    JavaScript:{}、new Object和Object.create的区别
    函数声明和函数表达式的区别
    Java基础重写override
    Java基础多态数组
    Java基础编译类型和运行类型(多态)
    Java基础继承的内存分析
    Java基础访问修饰符
    Java基础继承的使用
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/9193866.html
Copyright © 2020-2023  润新知