【BZOJ 1535] [Luogu 3426] SZA-テンプレート(KMP + +二重連結リストツリーを失敗)
フェイス質問
Byteasarが壁に描かれた長い文字が欲しい、彼はテンプレートとしてしばらくの文字の前部からそれを行うために傍受されました。文字の列は、彼が望んだ後、テンプレートは、その後、適切な場所にスプレー繰り返されます。文字を数回噴霧することができますが、場所が異なる文字を噴霧することはできません。テンプレートを作る努力の無駄ですので、彼は最小の長さがある見つけ、できるだけ小さな長さをステンシルしたかったです。
ababbababbabababbabababbababbabaのためのサンプルを取り、最初の8文字のababbabaのテンプレートは、プロセスを噴霧することである。ababbababbabababbabababbababbaba
分析
分析テンプレート文字列、我々は3つのプロパティがあります
テンプレート文字列プロパティ:
- テンプレートは、共通の接頭辞と接尾辞の文字列に必要なテキスト文字列Bであります
- テンプレートは、別のテンプレート文字列の文字列B(Aを覆うことができる、すなわちB)が存在する場合、Bは、溶液Aよりも優れています
- 文字列間のスペースの数は、完全にテキスト文字列B、次いでBが(初めに応じて計算された)位置に一致さを覆うことができるテンプレートは、Aの長さを超えていない場合
証明:
1.それは接尾語の前にない場合は、始まりと終わりが塗らことができないであろう
明らかにテンプレート文字列で定義された2
3.サンプルマップを描くか、見て、スプレーする際は、隣接または重複鋳型配列でなければなりません
本質的に1は、我々は条件を満たしたテンプレート文字列は、1〜次の[n]は、または次の[次の[N]]、次の[次の[次の[N]]] ...などのサブストリングでなければならないことがわかりました。
したがって、我々は、プレフィックス長を列挙することができ、および\(O(N)\)が決定されていますが、この複雑さはまだある\(O(N ^ 2) \) 、および最適化を検討します
その後、ツリーを失敗使用する必要があります。
木に失敗し、実際には、ミスマッチの位置でもツリーを取得します。我々は尊重し(iは[1、N - ] \で\)\を、偶数側\((次に[i]は、I)\) 、\(次に[i]が\)される(iは\)\父。そのための\(次[I]は、<私は\)、でも、ツリーの外にでなければなりません
明らかに、ツリーのルート失敗が0で、xは、ツリーのノードがテキスト文字列1〜Xビット、Xのサブストリングの長さを失敗表します。そして、1〜次の[n]は、または次の[次の[N]]、...、そのような部分文字列は、実際に木に次の[n]を失敗し、次の[[N]]、[次へ次へ]〜ストランド0です。
木の性質を失敗します。
共通部分は、サブストリングサフィックスyは点xが場合は、Xで表され、Y点はの祖先である場合にのみされて示される前
だから我々は、ノード鎖に0〜次の[n]のマークを付けることができ、ツリーのルートからDFSは失敗します。すべての選挙再帰息子ノードのチェーンでは、すべてのノードのサブツリー内の他の息子を削除します。最初に1〜n個の隣接する要素間の距離を維持する二重連結リストチェーン(1の初期距離、Xを削除し、ノードZとの間の距離の中央にY、Xを使用する場合、yはDISTなる(X 、Z)+ DIST(Y、Z))。
これは、から見やすいですテキスト文字列でテンプレートマッチング文字列の位置との間のスペースの数。鋳型配列3の性質に応じて、テンプレート文字列の長さのスペースの数を超えていません。スペースの数が、xが最小の長さである場合、ノードX及びXは、$ Xの\の$のGEQすることが再帰的に、サブストリングの長さを表します。
各ノードは、最も一度削除されているため、時間複雑度\(O(N)\)
広告:
コード
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 500000
using namespace std;
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int sz=1;
int head[maxn+5];
void add_edge(int u,int v){
sz++;
E[sz].from=u;
E[sz].to=v;
E[sz].next=head[u];
head[u]=sz;
}
int n;
char s[maxn+5];
int nex[maxn+5];
struct list{
int pre[maxn+5],nex[maxn+5];
int mv;
void ini(int n){
mv=1;
for(int i=1;i<=n;i++){
pre[i]=i-1;
nex[i]=i+1;
}
}
void del(int x){
pre[nex[x]]=pre[x];
nex[pre[x]]=nex[x];
mv=max(mv,nex[x]-pre[x]);
pre[x]=nex[x]=0;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
del(y);
}
}
inline int query(){
return mv;
}
}S;
int ans=0;
bool mark[maxn+5];
void dfs(int x){//x实际上是某个nex,代表前缀(模板串)长度
int to;
if(S.query()<=x){//模板串性质3
ans=x;
return;
}
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(mark[y]) to=y;
else S.del(y);//根据fail树的性质2,把不能匹配的部分去掉,得到两个匹配位置之间的最大距离
}
dfs(to);
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=2,j=0;i<=n;i++){
while(j>0&&s[j+1]!=s[i]) j=nex[j];
if(s[j+1]==s[i]) j++;
nex[i]=j;
}
for(int i=1;i<=n;i++) add_edge(nex[i],i);//建立fail树
S.ini(n);
for(int i=n;i>0;i=nex[i]) mark[i]=1;//模板串性质1
dfs(0);
printf("%d\n",ans);
}