これは強力なオートマトンである - サフィックスオートマトンは==>私は、オートマトンを理解することが最も難しい、ニウ・ベン、最も強力学びました
あなたのために今の質問:
文字列、どのようなものに発生するすべてのサブストリングを必要とする数の文字列を考えます
単純なアルゴリズム
①左の点の列挙は、統計の数に関するハッシュレコードで、右のポイントを列挙です。(正解率を確保するため、最高のダブルハッシュことに注意してください)
:予想時間複雑\(O(N ^ 2)\)
②直接オープントライレコードは、すべてのサフィックスを構築することができますすることはそうのように:aabbabd
たびに、レコード上に構築するに回数それぞれの場所、そこので\(N ^ 2 \) 、その後、時間の複雑ポイントである\(O(N ^ 2) \)
\(N ^ 2 \)非常に良い複雑さ、しかし...
N <= 100000ああああ....
あなたがこれを行うことはできません、どのようにしますか?!
その後、我々は、サフィックスオートマトン(サフィックスオートマトン)を導入します
(高エネルギーの前に、濃縮物)
サフィックスオートマトンは、定義されました
文字列\(S \)サフィックスオートマトン\((SAM)は、\)有限状態オートマトンである\((DFA)\) 、そしてそれは、すべて受け入れることができます\(S \)当然のサフィックス(、それを私たちは)はるかに限定されないサフィックスを行うことができます。
実際サフィックスオートマトンである\(DAG \) 、(有向非巡回グラフ)頂点、及びエッジは状態間の遷移を表す状態です。
状態\(S \)は、すべての残りの状態に到達することが可能である、初期状態と呼ばれています。
全自動搬送機は、有向エッジがあり、いくつかのシンボルが異なるマークを有していなければならないすべての転送のある状態から出発して、マークされています。
状態は最終状態と呼ばれ、初期状態では、我々がどうかを示す(S \)を\最終状態に来て任意のパスを介して、順次ラベル側を書き、元の文字列は、サフィックス文字列を取得する必要があります。
全てオートマトンで上記条件を満たし、サフィックスオートマトンの状態遷移は最少と状態の数と遷移サフィックスオートマトン有する()| | S(O \ \) 。
事前定義
これは、前のサフィックスオートマトンは、最も中心的なもの、最も困難な部分である、と言うことができます。だから、良い見てみましょう!
プレ[X]は、最も長い文字列を表し、[S〜X]サフィックス[S〜プリ[X]]例えば右端点の:
緑色の点線は、予めエッジを示しています。明らかに、私たちは事前に3辺が[S〜3]最長のサフィックス[S〜1]に接続されて見つけることができます右のエンドポイントです。
でも、前席側
以下のためには、ちょうど今、ラスエッジを事前に見つけることがポイント参加している場合、あなたがこれと同じ側の新しい現在のプラス側を見つけるまで、この1のプラス側にこれと同じ側縁の電流にない新しい加え、これまでのところ。次に、2つのタイプに分ける:我々は、Qに息子に接続し、電流設定点pを見つけます。
pは、qは2点間の距離である場合①、そのまま新しいノード現在指摘エッジqを事前にすることができます。
P場合、Q 1は2点間の距離よりも大きい場合、我々は、新しいノードが現在プリエッジ点Qである場合、[S〜のQ]を満たしていないこと②実測値[S〜今]接尾辞である(下記)
(AB&ABBではありません接尾辞)
我々は、AB bとして、元の文字列を持っているので、我々は(それもそのエッジの下に、である)、このエラーの理由を分析し、その間違いはABB ABサフィックスになったので、我々はそれを持っていたと思いましたでも、接尾辞をbにしたいので、これは、我々はいくつかの操作を追加する必要があります。
加点操作
我々は、pの後にポイントを追加し、pおよび新たに追加された点で、以前に接続されたエッジと、現在のシンボルのすべての点Qは、例えば、マップが原因で元の文字列のこの時点で、Bであり、現在のシンボルを同じ側に接続されています。存在しない、それだけでスペアのQされたので、それのすべての側面に出て、Qポイントは偶数でなければなりません、そして事前に新しく追加されたポイントは、自然の側のq元前エッジ、Qになり、今事前側は現在のプラス新しいノードを指すようになります:
その自動キャプテンこのため、「aabbabd」のために:
時間複雑
時間計算量は:\(O(N)\) 。
最初の三つの点に加えて、すべてのポイントを追加することができるので、状態の最大数は、2N-1である後の時点。
アプリケーション
1. 文字列T与えられ、各時間クエリpは、pはTサブ文字列かどうかを尋ねました。
- サフィックスオートマトンを構築するためのT。
- 常に文字列のクエリに沿って歩き、その後、初期状態のSSから行くようになりましたお願いします。
- 時間複雑\(O(| T | + Σ| P |)O(| T | +Σ| P |)\)
2. 文字列Sを考えると、それがどのように多くの異なるサブ文字列を尋ねました。
- これは、最初のサフィックスオートマトンを構築します。
- その後、任意のパスのサフィックスオートマトンは、異なる文字列です。
- だから、答えはS出発から始まる異なるパスの数です。
3. 指定された文字列S、毎回辞書小S kの異なるサブクエリ文字列のすべて。
- 最初の二つは似て尋ねると、私たちは、状態から各文字のパスの数を開始し対処する必要があります。
- そして、出発点からSだけで結構見てきました。
4. 文字列Sを考えると、それは同型辞書順最小の文字列をループ見つけます。
- 私たちは、サフィックスオートマトン列S + S、その後、辞書順最小の欲張り探索Jiuhaola〜を作ります
5. 複数のストリングを考えると、彼らは最長共通部分を見つけます。
- 考えます...
テンプレートコード
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxN 2000010
using namespace std;
int son[maxN][27],pre[maxN],len[maxN],sum[maxN];
int sz,las,lens,now,q,p;
char s[maxN];
void add(int x)
{
len[++sz]=len[las]+1,sum[sz]=1,now=sz;
for (p=las;p&&!son[p][x];p=pre[p]) son[p][x]=now;
if(p)
{
q=son[p][x];
if(len[q]>1+len[p])
{
len[++sz]=len[p]+1;
memcpy(son[sz],son[q],sizeof(son[q]));
pre[sz]=pre[q];pre[q]=pre[now]=sz;
for (;son[p][x]==q;p=pre[p]) son[p][x]=sz;
}
else pre[now]=q;
}
else pre[now]=1;
las=now;
}
int main()
{
scanf("%s",s+1);
lens=strlen(s+1);sz=las=1;
for (int i=1;i<=lens;++i) add(s[i]-96);
}