一點對后綴自動機的理解 及模板


題目描述

給定一個只包含小寫字母的字符串SS,

請你求出 SS 的所有出現次數不為 11 的子串的出現次數乘上該子串長度的最大值。​​

 

 

先講講對后綴自動機的理解:

后綴自動機就是后綴樹倒過來的樣子,很形象. 

如ACADD:

其構造的思想大致是:

1.首先將點分為一些類別,其中有一些是接受點,也就是說走到接受點的都是原串的后綴,而接受點不止一個,所有的接受點就組成了所有后綴的集合.

2.當新加入一個字母c,那么原串的結尾就會產生變化,也就是說接受點要發生改變,我們就要想辦法將當前的接受點的集合轉移成新的集合,因為后綴自動機要保證狀態數最小化,所以我們要盡量利用以前的狀態來產生新的狀態,所以當他的父親節點含有c這個轉移時,我們就直接討論能否直接利用即可,注意:這里的父親是指接受點集合之間的關系,也就是說沿着父親節點走依舊會是接受點.

 

他的構造處有一些難理解的地方,比如len[q]==len[p]+1 和 len[q]>len[p]+1的討論

如此圖中

 

如此圖中,在構造第二個A時 起始p為C 然后跳到了root 構造A時由於len[q]==len[p]+1 (p為root,q為第一處的A)那么就直接把A接到C后面,因為這種情況下q的路徑必然經過p,可以腦補一下.

另一種就是插入D時 len[q]>len[p]+1 的情況,可以簡單的想想,如果直接把D接到q指針的D后面,那么DD這個后綴將無法走出

所以要新建一個D強行滿足第一種情況,就可以滿足條件了

 

按這個例子的理解:如果len[q]!=len[p]+1 那么p-q中間存在一條路徑,且只有這條路徑上的點可以直接到達D.那么就會漏掉一些后綴.

嚴格的理解:

因為我們的目標是做到狀態的最小化,所以要盡量利用之前的狀態,那么如果len[q]==len[p]+1,那么就可以直接利用.

如果len[q]>len[p]+1,一個狀態接受的字符串的長度是連續的,那么p-q之間還存在其他更長的后綴,我們要保證在新的后綴建立的情況下,原來的狀態不能丟失,所以要新建節點,產生兩個分支,這也是要把q復制到新節點上的原因,q能走到的地方,新節點都能走到,此時q的作用就是接受之前更長的子串.

 

重要性質:

1.設當前點為p,那么fa[p]是以p結尾的字符串的集合的最大子集,且不重復(根據構建方法可以腦補)

2.每一個節點內代表的字符串的長度是一段連續的區間[minlen,maxlen],且 maxlen[fa]+1=minlen

利用這個性質就可以解決這題,可以在parent樹上直接dp,fa[p]存在的串,p也一定存在,那么就可以直接累加了

 

 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 using namespace std;
 8 typedef long long ll;
 9 const int N=1e6+5,M=2e6+10;
10 char s[N];int cur=1,cnt=1,n,last,ch[M][27],fa[M],dis[M],size[M];ll ans=0;
11 void build(int c,int id){
12     last=cur;cur=++cnt;
13     int p=last;dis[cur]=id;
14     for(;p && !ch[p][c];p=fa[p])ch[p][c]=cur;
15     if(!p)fa[cur]=1;
16     else{
17         int q=ch[p][c];
18         if(dis[q]==dis[p]+1)fa[cur]=q;
19         else{
20             int nt=++cnt;dis[nt]=dis[p]+1;
21             memcpy(ch[nt],ch[q],sizeof(ch[q]));
22             fa[nt]=fa[q];fa[q]=fa[cur]=nt;
23             for(;ch[p][c]==q;p=fa[p])ch[p][c]=nt;
24         }
25     }
26     size[cur]=1;
27 }
28 int c[N],sa[M];
29 void flower(){
30     for(int i=1;i<=cnt;i++)c[dis[i]]++;
31     for(int i=1;i<=n;i++)c[i]+=c[i-1];
32     for(int i=cnt;i>=1;i--)sa[c[dis[i]]--]=i;
33     for(int i=cnt;i;i--){
34         int p=sa[i];
35         if(size[p]>1)ans=max(ans,(ll)size[p]*dis[p]);
36         size[fa[p]]+=size[p];
37     }
38 }
39 void work(){
40     scanf("%s",s+1);n=strlen(s+1);
41     for(int i=1;i<=n;i++)build(s[i]-'a',i);
42     flower();
43     printf("%lld\n",ans);
44 }
45 int main()
46 {
47     work();
48     return 0;
49 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM