おそらく、より良い読書体験
EDITORIAL
このブログはスプレーしないで好きではない、主に自分の理解のいくつかについてである
約見つかっオンラインブログを\(右\)ことの理由を明らかにせずに行う方法について話を収集し、学生がそれを記録した後、長い間議論し
理解していない場合は、場所や間違った場所には、質問をしたり指摘する歓迎
あなたはブロガーが答えてくれる、求められることがあります他の質問を
分析する様々な方法の理解このブログのコンテンツにとに深いので、社長の少しの部分
だけであれば既知の方法、中間反射スキップ可能
図は、サフィックスオートマトンを高く評価しました
- \(親\)ツリーノードとサフィックスオートマトン内のノードがまったく同じですが、側面は同じではありません
- 以下からのルートを開始するすべてのサブ文字列から抜け出すために、そして唯一のサブストリングのうち
- \(endposの\)出現位置の最後の文字位置に出現するすべての文字列の位置を表しコレクションです
- \(右\)を表す設定\(endposの\)同じサブ・セット、\(右\)のセットは、サブストリングの複数によって表すことができる、文字列のサブ長さは連続的であり、接尾辞の関係を持っている必要があり、\(右\) 、それは表現の大きさを表していることである(endposの\)\のコレクションのサイズ、つまり、\(endposの\)要素の集合数
二つの\(右\)の関係が包含関係、あるいは互いに独立の2つだけ、のどちらかである、ない交差点は存在しません
両方の文字列が同じ持っている場合は、\(endposのを\) 、その後、別の部分文字列が存在する必要があります
- \(右\)セット\(R1 \)は別であってもよい\(右\)セット\(R2 \)と、この時間が満足しなければならない\(R2 \)にされ(R1 \)を\サフィックス
- 2によると、我々はルートからサフィックスオートマトン上で実行するために始めることができ、あなたがポイントポイントに行き、すべてのサブ文字列の外に実行することができます\(右\)サイズ、また、これに現在のマッチが言いました文字列の出現
\(親\)ツリーは、サフィックスオートマトンを構築することができる、各ノード\(失敗\)接尾文字列を示すノードでなければならない、なぜなら\(失敗\)ノード\(右\)を含むコレクションノード。
なぜ?サフィックスオートマトンが、これは他のdalaoを見るために詳細に説明することができますどのように出て構築されたツリーを構築することであるため、この記事では非常に重要話します
サフィックスオートマトンノードはノードで表されている端サフィックスの
自動機の構築
先に述べた、私は配列のバージョンをプレイしています
約束
(\ \ {整列}始める&
\ FAの\ RIGHTARROWが失敗\\&\右\端{整列}のLEN \ RIGHTARROW最長\長\\&サイズの\ RIGHTARROWサイズ\) 元の文字列、古いマスター列のメインストリングプレフィックスとしてプレフィックスがどのノードの自動機を追加した後
、さらに\(サイズ\)は、サフィックスオートマトンの構築には影響しません、それは無視することができ、我々はに後で話します\(親\)とき言う\(サイズ\)
コードで書かれた主な内容は、意志理解するために上記の内容を繰り返して
、あなたがそれを見て、またはCSDNを読むために彼自身のエディタを開いてリッピングすることができ、ここで醜いコメントを感じる場合は、トップへのリンクがあります
#include <cstdio>
#include <cstring>
const int maxn = 2000006;
const int maxc = 27;
int tot=1,last=1;//last -> 旧主串的节点
int fa[maxn],len[maxn],size[maxn];
//fa -> fail fa[x]的right集合一定包含x fa[x]一定是x的后缀
//len[x] -> x为后缀最长串长度
//size[x] -> x 号节点表示的right集合的大小
int son[maxn][maxc];//son[p][c] -> 在p所代表的集合后加c字符,该字符c是哪个节点 亦可认为是边
//1 号节点为初始节点 初始节点没有fa
//{{{构建SAM
void extend (int c)
{
int p=last,np=++tot;
last=tot,len[np]=len[p]+1;
while (p&&!son[p][c]) son[p][c]=np,p=fa[p];//跳后缀 因为是旧主串的后缀 它们全都可以加一个c
//当前p没有c,表示该子串是第一次出现,p向np连一条c边
//若当前p有c了表示后面的fa所表示的后缀有节点集合表示了以c为结尾的后缀了(因为曾经出现过),此时出现了两个以c结尾的right集合
if (!p) fa[np]=1;//表示c从未出现过 它的后缀为空
else{
//要处理这两个以c为末尾的节点
int q=son[p][c];
if (len[q]==len[p]+1) fa[np]=q;//q是新主串的后缀
else{
//即len[q]>len[p]+1
int nq=++tot;//不是新主串的后缀 因为p是新主串的后缀 而len[q]>len[p]+1且q还没被跳过(若是其后缀,按理说应该先被跳到
len[nq]=len[p]+1;//p的endpos多了个n 所以要新节点 表示由p+c得到的后缀 即nq
fa[nq]=fa[q];//nq只是endpos变多 其样子仍是原来那样 其后缀仍是原来的后缀
fa[np]=fa[q]=nq;//nq 是 q的后缀 也是新主串的后缀
memcpy(son[nq],son[q],sizeof(son[q]));
while (son[p][c]==q) son[p][c]=nq,p=fa[p];//p的后缀的endpos也多了个n
}
}
size[np]=1;//该节点right大小初值赋值为1
}
//}}}
\(右\)コレクション
探している\(右\)コレクションのサイズを
私たちは知っている、\(右\)で\(親\)ツリーが包含関係の品種です
ので、キープ\(親\)の需要\(右\)コレクションサイズ
最初のメインの文字列\(右\)のコレクションを1の初期値の大きさ、サフィックスオートマトン構築する際のプロセスが完了した
A \(右\)自分の息子であるコレクションのサイズ\(右\)コレクションのサイズと元のサイズで(おそらく1 )
\(右\)のない交差点のセット、ノードメインストリング\(右\)セットサイズが1であります
エネルギーの唯一の主なポイントの文字列表現は、リーフノードであるが、リーフノード\(右\)コレクションは、メインの文字列上の点を表すだけでなく、文字列がリーフノード上の主なポイントは、必ずしもではない
理由を
理解の2種類
- \(親\)による木の側\(FAは、[I] \)に接続されている(iは\)\
セカンダリノードを開く他あり(FA \)\、それはリーフノードではありません - ノードは、文字列のマスタノードである必要があり、それは一度だけ発生した場合、メインストリング(プレフィックス)のサブストリング端と(メインストリングのサフィックスである)、それより長くなければならない、任意の部分文字列を考える
ように構成メインの文字列のみ自動機\(サイズ\)与えられた初期値は1で
、その後、メインの文字列を繰り返すことができる、それが息子を持って、そしてなぜ、まだそれを1の初期値を与えます - 文字列の場合は\(ABAC \) 、私たちは何を描く\(親\)ツリーが
あります\(右\)小に集合\(右\)第4ノードまで(第2の欠落要素がある場合にセットしノード)
この場合は、最初の文字が後ろに表示されていた場合である
後方の任意の点を考慮することが\(endposの\)セット、その接尾辞\(endposの\)場合、必ずしも備えるセット\(endposの\)の要素の数は、その文字列のメインノードで、1ないが1、それは息子の持っている必要があります\(endposのを\)それはより少ない要素
この文字の前面だけで、それは息子ではない、何の接尾辞ではありません\ ({1} \)このセットの、その初期値が1で与えられた問題はないように、一つ少なく要素と言うことです - 上記の延長の場合は、メインの文字列の繰り返し(すなわち、接頭辞が中央に表示されます)場合は、として、\(abcab \)
その場合の最初の文字、ではありません\(AB&\)\(右\)息子のコレクションが失われました\({2} \)
それは接尾辞を持っていないので、ケースの上に、そして場合は、その接尾辞でした\(右\)コレクションのいずれかの父、どちらか、それは同じである
ので、それは最初に表示される場所、それが失われます
したがって、初期値が正しいとが必要であるメイン列車1に割り当てられています
だから我々がしなければならないことは構築することです\(親\) 、その後、ツリーを上記に再び実行し(DFS \)\
我々はそれがあるので、また、当然のことを直接再帰することができます\(DAG \) 、トポロジーを計算することが可能です大きい長さが自然に、それは父親の息子によって考慮され、\(親\)ツリーが下位置、降順長さに応じて、このステップを選択することができている(ソート\)\、基数ソートがあってもよい
\ (\ mathcal {コード} \)
for (int i=2;i<=tot;++i) add(fa[i],i);//因为根节点没有fa,所以要从2开始枚举,当然也可以设根节点为0,那么还得给根节点的fa赋初值为-1,上面特判也得改一下
dfs(1);
void dfs (int p)
{
for (int e=head[p];e;e=nxt[e]){
dfs(to[e]);
size[p]+=size[to[e]];
}
}
//基数排序 常数较小
for (int i=1;i<=tot;++i) ++cup[len[i]];
for (int i=1;i<=n;++i) cup[i]+=cup[i-1];
for (int i=1;i<=tot;++i) mp[cup[len[i]]--]=i;
for (int i=1;i<=tot;++i) size[fa[i]]+=size[i];
\(右\)のコレクションを理解します
\(右\)コレクションは個人的には素晴らしいと思い
、我々は見つけるでしょう、(右\)\コレクションのサイズは、文字列のみのマスターノードによって更新されたときに、文字列に遭遇マスターノード(サイズ\)を\が 1つ増加し
、他の言葉で、ノード\(右\)セットのサイズは、(それ自体を含む)のサブツリーに等しく、ノードの数は、文字列のマスターノードである
ことが理解されている方法文
- サブは、メインの文字列(接頭辞)接尾辞でなければなりません
- それぞれの主な文字列(接頭辞)は同じではありません
次いで、ノード\(右\)セットのサイズは、接尾番号接頭文字列に等しいです
したがって、\(親\)ツリーノード\(右\)セットのサイズのように表すことができる
\(endposの\)のために(右\)\の対応するセット\(endposの\)文字列の集合でありますサフィックスプレフィックス番号は
、サブツリーノードの数をメインの文字列を表すことができます
アプリケーションサフィックスオートマトン
部分文字列かどうかを判断します
2によります。
以下からのルートを開始するすべてのサブ文字列から抜け出すために、そして唯一のサブストリングのうち
サフィックスオートマトンで再度実行するには、ルートノードから始まります
トライ木は、一時停止または殴られました
異なる部分文字列の数
この二つの方法があります
私たちは、文字列の共有複数あってもよいことを知っている\(右\) 、文字列の長さは確かにありません同じで、同じシリーズである、そして我々は同じ\(LEN [i]が-len [ FA [I]] \) すなわち、これは、誘導されてもよい\(右\)ので総数は異なるサブストリングの数であり、シーク時間れる各ノードに対して、複数の文字列のシェアの収集を
必要とする\(\ sum_ {i = 1 } ^ {TOTを} (LEN [i]が-len [FA [I]])\)考えてみましょう(DP \)\上記によると、2を、私たちは、全体のサフィックスオートマトンはサイドを実行しているルートノードから開始することができ、ノードに直面したときにプラス答えを与えます
異なるサブシリーズは、このような定義になる場合は
別のサブ文字列のサブストリング演算子の同じ位置は異なるが
、我々は7を考慮することができます。
2によると、我々はルートからサフィックスオートマトン上で実行するために始めることができ、あなたがポイントポイントに行き、すべてのサブ文字列の外に実行することができます\(右\)サイズ、また、これに現在のマッチが言いました文字列の出現
まず、各見つける(右\)\サイズを、答えるためにノードを直面プラス\(サイズ\)へ
K-少年文字列
このノードからどのくらいの部分文字列を記録し、その後することに\(スプレイ\) 、実行することができますように
ll dfs (int x)//先处理出还有多少子串
{
if (num[x]!=-1) return num[x];
num[x]=size[x];
for (int i=1;i<=26;++i)
if (son[x][i]) num[x]+=dfs(son[x][i]);
return num[x];
}
void kth (int x,ll k)//求第k小
{
if (k<=size[x]) return;
k-=size[x];
for (int i=1;i<=26;++i){
if (son[x][i]){
if (k<=num[son[x][i]]){
printf("%c",i+'a'-1);
kth(son[x][i],k);
return;
}
else k-=num[son[x][i]];
}
}
}
最初の探している\(k個\)だけのサイクルからの大きな変更\(26 \)する(1 \)を\を列挙することができます
最小の循環シフト(最小表記)
コードの表現は、どのような最小のサフィックスオートマトンで、シンプルで短く、
私たちは、\(S + S \)うち建てサフィックスオートマトン
、その後は、最小の長さを見つけるためにある(|よ| \)\をのサブ
上記と同じコードを...
それはそれのそんなに話す
(その後、半書かれた新しい問題を発見し、数日にしたい)数日間書くことに一生懸命に
賞賛にそれを与えるために