サフィックスオートマトン感情的な理解

サフィックスオートマトン感情的な理解

サフィックスオートマトンは、それがやっているかわからない、それは少し無知かもしれないが、実際の大規模なセクションを参照するには、特に直接証拠十分に理解されていません

それは私が私の良い感情的な理解を紹介しましょう、私たちはこの記事を見て、他の人のブログQAQをより良く理解するかもしれません

フロントチーズ:トライ木

理解しやすいように、それが偽物であるため、偽の接尾辞木($ N ^ 2 $)の話をする前に、恐れることはありません。

図は、.JPGを盗みます

文字列(例:について\(バナナだった\) )、それのサフィックス($バナナは、アナナス、ナナス、アナス、NAS、AS、Sました\()トライ木に挿入され、そして最後にマークされていますエンドマーク)は、\暴力の接尾辞であるトライ(\、明らかに時間と空間の複雑です\) O(N- ^ 2)$

それが大幅に最適化されたオートマトン接尾辞、すなわち、サブストリングと、圧縮などの操作を繰り返しためサフィックスの時空ディップの複雑さと言うことができる\(O(N)\) とを有します接尾辞木の一部のプロパティ

理解サブストリング:文字列Sのサブのために、それはそのあなた((Sの接尾辞)接頭辞)、何の問題として認識されるであろう

サブ・トライSにおけるパス文字列である場合、定数は、ルートから開始されていること

それを暗く

  • サブストリングの出現を探す:つまり、そのマークの終わりと下に、トライしてそのパスを見つけるために、それはプレフィックス番号の接尾辞であります
  • 最初のサブストリングを見つける(最後)の発生位置は、上記の、最大深さ(奥行き最小値)マーク端を歩いています
  • 統計的性質の異なるサブストリング、即ち数、トライノードツリーの数

彼は、サフィックスオートマトンにカットし始めました。

サフィックスオートマトン魔法エッジのさえ多くは、成功した空間と時間を削減します

開始ノードから開始し、終了ノード以上であってもよいし、パス終端ノードは、元の文字列が接尾辞である任意のパスを取る:彼は少し性格を持っていました

最初は自然の理解を容易にするためにzjpの首長からの画像、ブログを投稿しました:

いくつかの重要な要素:

endpos(x): 它是一个集合, 表示一个子串的所有结束位置(可能有许多结束位置, 因为会有本质相同子串), 如果两个子串的\(endpos\)相同, 那么这两个子串属于一个"状态" , 同时他俩一个是另一个的后缀

len(x) : 对于一个状态所表示的一堆字符串, 他们最长的那个的长度, 同时这些字符串按长度排个序, 长度是连续的整数

后缀link:

设一个A状态如("abab", "bab", "ab") 那么"b"就是状态中没有的最长后缀, 即"abab"(或bab"等)的最长的且没有在该状态出现的后缀

那么我将A状态向"b"状态所在的B状态连有向边, 叫做link, 如果从一个状态不断的跳link, 那么就会遍历一个字符串的所有后缀

转移函数: 在一个状态的末尾加一个字符使它转向另一个状态, 可以证明在同一个状态的字符串在末尾加一个字符后还在同一个状态

下面来讲构造:

考虑从前往后一个一个加入字符, 即增量法, 这样就保证了每加一个字符都满足后缀自动机的性质

设当前最长串为S[1...i-1], 现在加一个字符S[i], 我们要干的事就是让它的所有后缀都能从起点开始走一条路径表示出来

S[1...i]肯定是一个新状态, 因为他是最长的, 设这个转态为np

因为前面S[1...i-1]已经构造好了, 我们从状态p = {s[1...i-1]}开始往前跳, 刚才说了, 往前跳的过程中会遍历它的所有后缀, 那么我们直接从以前的状态向他连一条边, 就可以从以前的状态转移到他了, 虽然这还是O(\(n^2\))的错误解法, 但给我们提供了不错的思路

设ch[s]['a'~'z']为它的转移函数, 如ch[s]['a']表示从s状态加一个'a'字符转移到哪个转态

设加入字符c, 向刚才一样跳link, 设到了状态A, 如果ch[A][c] == 0, 直接让ch[A][c] = np, 然后继续跳link, 最后如果跳到了根节点, 那他的link就是初始状态

    for (; p && !ch[p][c]; p = f[p]) ch[p][c] = np;
    if (!p) f[np] = 1;

如果碰到了ch[A][c] = q(\(q \not= 0\)), 分两种情况

如果len(p) = len(q) + 1, 那么使q成为终止状态, np向q link一下

否则, 原先p中的字符串集就不再有相同的\(endpos\), 因为从A转移过来的串也是S[1...n]的后缀, 所以这部分\(endpos\)会多一个(i), 这个状态就会分裂, 因此我们新建一个状态\(nq\)去让多出的部分转移, \(p\)将转移到\(nq\), \(nq\)再转移到\(np\), 同时\(q\)\(np\)都将向\(nq\) \(link\)

看看图理解一下(大佬讲的很好)

代码:

void add(int c) {
    int p = las, np = las = ++cnt; zhi[cnt] = 1;
    len[np] = len[p] + 1;
    for (; p && !ch[p][c]; p = f[p]) ch[p][c] = np;
    if (!p) f[np] = 1;
    else {
        int q = ch[p][c];
        if (len[q] == len[p] + 1) f[np] = q;
        else {
            int nq = ++cnt;
            for (int i = 0;i < 26; i++)
            ch[nq][i] = ch[q][i];
            len[nq] = len[p] + 1, f[nq] = f[q];
            f[q] = f[np] = nq;
            for (; p && ch[p][c] == q;p = f[p]) ch[p][c] = nq;
        }
    }
}

おすすめ

転載: www.cnblogs.com/Hs-black/p/12004609.html