字尾自動機 板子
阿新 • • 發佈:2019-01-04
字尾自動機
並沒有搞的很清楚,憑著當前的理解胡亂bb兩句得了,內容存在誤導,請仔細甄別。
endpos
集合每個子串結尾的位置集合。
本質相同
endpos
相等的子串集合然後這個本質不同的集合構成自動機的狀態。
\(substr(i)\),狀態\(i\)包含的子串集合
\(len(i)\),狀態\(i\)包含的最長子串
性質:狀態\(i\)包含的子串是最長子串長度連續的一段字尾。
字尾連線(\(parent\)邊)
狀態\(i\)最小子串的字尾為什麼不在裡面呢?
因為在別的地方出現了字尾,被分到別的狀態了。
那麼當前狀態對這個別的狀態連邊即是字尾連線。
構造流程(線上增量法)
新建一個點,然後走上一個點的parent邊,如果狀態未對當前連邊,連上。
1步驟在根結束,par邊連虛根1
1步驟在\(p\)結束
- 如果\(p\)的後面的點與\(p\)長度差一個,\(par\)指中國後面的點,這裡沒有產生新狀態
- 不止差一個,\(p\)的末尾狀態掉了,再開一個狀態存一下,然後把東西繼承一下,然後舊狀態抵回去,最後新點再更新一下資訊。
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::max; const int N=2e6+10; int head[N],to[N],Next[N],cnt; void add(int u,int v){to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;} int tot=1,las=1,ch[N][26],siz[N],par[N],len[N],n; ll ans;char s[N]; void extend(int c) { int now=++tot,p=las; len[now]=len[las]+1,siz[now]=1; while(p&&!ch[p][c]) ch[p][c]=now,p=par[p]; if(!p) par[now]=1; else { int x=ch[p][c]; if(len[p]+1==len[x]) par[now]=x; else { int y=++tot; len[y]=len[p]+1,par[y]=par[x]; memcpy(ch[y],ch[x],sizeof(ch[x])); while(p&&ch[p][c]==x) ch[p][c]=y,p=par[p]; par[now]=par[x]=y; } } las=now; } void dfs(int now) { for(int i=head[now];i;i=Next[i]) dfs(to[i]),siz[now]+=siz[to[i]]; if(siz[now]>1) ans=max(ans,1ll*siz[now]*len[now]); } int main() { scanf("%s",s+1),n=strlen(s+1); for(int i=1;i<=n;i++) extend(s[i]-'a'); for(int i=2;i<=tot;i++) add(par[i],i); dfs(1); printf("%lld\n",ans); return 0; }
2019.1.4