后缀自动机 笔记

来源:互联网 发布:易语言写软件 编辑:程序博客网 时间:2024/06/08 09:26

参考了hihocoder和clj的课件,看了看hzwer的代码,懂了些东西,记一下。

  1. 后缀自动机是一棵trie树

  2. 给出一个字符串S,对于S的一个子串s,Right(s) 代表一个集合,为s在S中所有出现的结束位置集合。
    以S=”aabbabd”为例,Right(“ab”) = {3, 6},因为”ab”一共出现了2次,结束位置分别是3和6。同理Right(“a”) = {1, 2, 5}, Right(“abba”) = {5}。

  3. 对于一个字符串集合,若这个集合中所有的字符串拥有相同的Right集合,那么我们可以给这个集合一个状态s, 用Substring(s)代表这个集合,这个集合也对应着一个Right(s)集合。

那么现在就存在一个s <-> Substring <-> Right集合的一一对应关系。

对于Substring(s),存在longest(s)和shortest(s)分别代表Substring(s)中的最长和最短字符串,其长度分别为l(s)与r(s)。

对于一个longest(s),我们不断地去除他的第一个字符,直到其变为shortest(s),这个过程中出现的l(s)- r(s) + 1个字符串必然都属于Substring(s)。

若我们将shortest(s)的第一个字符删去(此时当然是假定shortest(s)不为空串),必然会导致状态的改变,因为此时的Right集合变大了,而s和Right(s),Substring(s)一一对应。

具体可以参照下图。
这里写图片描述

我们用fa(s)来代表从s进行的一次跳转到达的新状态。

若把状态按fa来建图,显然可以得到一个树形结构,我们称之为parent树。
CLJ课件附图

对于这棵树存在以下性质:
1. max(fa(s))=min(s)1
2. Right(s)Right(fa(s))

定义ST(T)为从空串状态走了字符串T到达的状态,trans[p][c]代表从p状态添加新的字符c能够到达的状态。

若干个状态作为点,构成了后缀自动机,这个后缀自动机借由trans组成了trie树,而这个trie树上的部分节点又类似AC自动机的fail那样构成了parent树。

构造方法CLJ课件已经给出了,这里挂上代码并标以注释。

namespace SAM{    static const int maxnode = 2e6+10;//至少开两倍    static const int maxn = 26;    int p, q, np, nq;    int cnt, ST_T;    int trans[maxnode][maxn], l[maxnode], fa[maxnode];    int newnode() {        int x = ++cnt;        memset(trans[x], 0, sizeof trans[x]);        fa[x] = 0;        l[x] = 0;        return cnt;    }    void init() {        cnt = 0;        ST_T = newnode();    }    void add(int c) {        //令当前串为T,新加的字符为x。        p = ST_T; np = ST_T = newnode(); l[np] = l[p] + 1;//令p = ST(T),新建np = ST(Tx)        while(!trans[p][c]&&p) trans[p][c] = np, p = fa[p];//对于p的所有没有标号c的边的祖先v,trans[v][c] = np。        if(!p) fa[np] = 1; //找到p的第一个祖先vp,他有标号c的边,如果没有这样的vp,那么fa[p]=root,结束该阶段。        else {            q = trans[p][c];//令q=trans[vp][c]            if(l[p] + 1 == l[q]) fa[np] = q;//若l[q] = l[vp] + 1,令fa[np] = q,结束该阶段。            else {                nq = newnode(); l[nq] = l[p] + 1;//否则建立新节点nq                memcpy(trans[nq],trans[q],sizeof(trans[q]));//trans(nq, *) = trans(q, *)                fa[nq] = fa[q];                fa[np] = fa[q] = nq;                while(trans[p][c] == q) trans[p][c] = nq, p = fa[p];//对于所有的trans(v, c) == q的p的祖先v, trans(v, c)改为nq。            }        }    }    void build(char *str) {        int len = strlen(str);        for(int i = 0; i < len; i++)            add(str[i]-'a');    }};
原创粉丝点击