日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【后缀自动机】hihocoder1445 后缀自动机二·重复旋律5

發布時間:2024/8/22 编程问答 38 如意码农
生活随笔 收集整理的這篇文章主要介紹了 【后缀自动机】hihocoder1445 后缀自动机二·重复旋律5 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

解題方法提示

小Hi:本周的題目其實就是給定一個字符串S,要求出S的所有不同子串的數目。小Ho你知道如何快速求解么?

小Ho:我們最近在討論后綴自動機,所以肯定是和后綴自動機有關!根據上周學習的SAM的基本概念和性質,SAM的每個狀態st都包含了一部分S的子串,記作substrings(st),并且(1)對于兩個不同狀態u和v,包含的子串substrings(u) ∩ substrings(v) = ∅; (2)每個子串都恰好被一個狀態包含。所以我們只要構造出來S對應的SAM,再對所有狀態st求Σ(maxlen(st)-minlen(st))就是子串的數目。

小Hi:沒錯。上周我們提到SAM有O(length(S))的構造法。這周我們就來講一講如何構造。

小Hi:首先,為了實現O(length(S))的構造,我們對于每個狀態不能保存太多數據。例如substring(st)肯定是沒法保存下來了。對于狀態st我們只保存如下數據:

數據 含義
maxlen[st] st包含的最長子串的長度
minlen[st] st包含的最短字串的長度
trans[st][] st的轉移函數
slink[st] st的Suffix Link

小Hi:其次,我們用增量法構造S對應的SAM。我們從初始狀態開始,每次添加一個字符S[1], S[2], ... S[N],依次構造可以識別S[1], S[1..2], S[1..3], ... S[1..N]=S的SAM。

小Hi:假設我們已經構造好了S[1..i]的SAM。這時我們要添加字符S[i+1],于是我們新增了i+1個S[i+1]的后綴要識別:S[1..i+1], S[2..i+1], ... S[i..i+1], S[i+1]。 考慮到這些新增狀態分別是從S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)通過字符S[i+1]轉移過來的,所以我們還要對S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)對應的狀態們增加相應的轉移。

小Hi:我們假設S[1..i]對應的狀態是u,等價于S[1..i]∈ substrings(u)。根據上周的討論我們知道S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)對應的狀態們恰好就是從u到初始狀態S的由Suffix Link連接起來路徑上的所有狀態,不妨稱這條路徑(上所有狀態集合)是suffix-path(u->S)。

小Hi:顯然至少S[1..i+1]這個子串不能被以前的SAM識別,所以我們至少需要添加一個狀態z,z至少包含S[1..i+1]這個子串。

小Hi:首先考慮一種最簡單的情況:對于suffix-path(u->S)的任意狀態v,都有trans[v][S[i+1]]=NULL。這時我們只要令trans[v][S[i+1]]=z,并且令slink[st]=S即可。

小Hi:例如我們已經得到"aa"的SAM,現在希望構造"aab"的SAM。如下圖所示:

小Hi:此時u=2,z=3,suffix-path(u->S)是桔色狀態組成的路徑2-1-S。并且這3個狀態都沒有對應字符b的轉移。所以我們只要添加紅色轉移trans[2][b]=trans[1][b]=trans[S][b]=z即可。當然也不要忘了slink[3]=S。

小Ho:那要是suffix-path(u->S)上有一個節點v,使得trans[v][S[i+1]]!=NULL怎么辦?

小Hi:好問題。我們以下圖為例,假設我們已經構造"aabb"的SAM如圖,現在我們要增加一個字符a構造"aabba"的SAM。

小Hi:這時u=4,z=6,suffix-path(u->S)是桔色狀態組成的路徑4-5-S。對于狀態4和狀態5,由于它們都沒有對應字符a的轉移,所以我們只要添加紅色轉移trans[4][a]=trans[5][a]=z=6即可。面對S時我們遇到了小Ho你提出的問題,trans[S][a]=1已經存在,怎么辦?

小Ho:怎么辦呢?

小Hi:不失一般性,我們可以認為在suffix-path(u->S)遇到的第一個狀態v滿足trans[v][S[i+1]]=x。這時我們需要討論x包含的子串的情況。如果x中包含的最長子串就是v中包含的最長子串接上字符S[i+1],等價于maxlen(v)+1=maxlen(x),比如在上面的例子里,v=S, x=1,longest(v)是空串,longest(1)="a"就是longest(v)+'a'。這種情況比較簡單,我們只要增加slink[z]=x即可。

小Hi:如果x中包含的最長子串 不是 v中包含的最長子串接上字符S[i+1],等價于maxlen(v)+1 < maxlen(x),這種情況最為復雜,不失一般性,我們用下圖表示這種情況,這時增加的字符是c,狀態是z。

小Hi:在suffix-path(u->S)這條路徑上,從u開始有一部分連續的狀態滿足trans[u..][c]=NULL,對于這部分狀態我們只需增加trans[u..][c]=z。緊接著有一部分連續的狀態v..w滿足trans[v..w][c]=x,并且longest(v)+c不等于longest(x)。這時我們需要從x拆分出新的狀態y,并且把原來x中長度小于等于longest(v)+c的子串分給y,其余字串留給x。同時令trans[v..w][c]=y,slink[y]=slink[x], slink[x]=slink[z]=y。

小Ho:好像比較復雜。

小Hi:我們來舉個例子。假設我們已經構造"aab"的SAM如圖,現在我們要增加一個字符b構造"aabb"的SAM。

小Hi:當我們處理在suffix-path(u->S)上的狀態S時,遇到trans[S][b]=3。并且longest(3)="aab",longest(S)+'b'="b",兩者不相等。其實不相等意味增加了新字符后endpos("aab")已經不等于endpos("b"),勢必這兩個子串不能同屬一個狀態3。這時我們就要從3中新拆分出一個狀態5,把"b"及其后綴分給5,其余的子串留給3。同時令trans[S][c]=5, slink[5]=slink[3]=S, slink[3]=slink[6]=5。

小Hi:整個過程的代碼如下,其中狀態0代表初始狀態S;狀態u, v, x, y, z的意義如上文所述;-1代表slink或者trans不存在。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
#define MAXL 1000000
#define MAXC 26
char s[MAXL+10];//文本串
int len/*文本串長度*/;
struct SAM{
int n/*狀態數0~n-1*/,maxlen[2*MAXL+10],minlen[2*MAXL+10],trans[2*MAXL+10][MAXC],slink[2*MAXL+10];
int new_state(int _maxlen,int _minlen,int _trans[],int _slink){
maxlen[n]=_maxlen;
minlen[n]=_minlen;
for(int i=0;i<MAXC;++i){
if(_trans==NULL){
trans[n][i]=-1;
}
else{
trans[n][i]=_trans[i];
}
}
slink[n]=_slink;
return n++;
}
int add_char(char ch,int u,int pos){
if(u==-1){
return new_state(0,0,NULL,-1);
}
int c=ch-'a';
int z=new_state(maxlen[u]+1,-1,NULL,-1);
int v=u;
while(v!=-1 && trans[v][c]==-1){
trans[v][c]=z;
v=slink[v];
}
if(v==-1){//最簡單的情況,suffix-path(u->S)上都沒有對應字符ch的轉移
minlen[z]=1;
slink[z]=0;
return z;
}
int x=trans[v][c];
if(maxlen[v]+1==maxlen[x]){//較簡單的情況,不用拆分x
minlen[z]=maxlen[x]+1;
slink[z]=x;
return z;
}
int y=new_state(maxlen[v]+1,-1,trans[x],slink[x]);//最復雜的情況,拆分x
slink[y]=slink[x];
minlen[x]=maxlen[y]+1;
slink[x]=y;
minlen[z]=maxlen[y]+1;
slink[z]=y;
int w=v;
while(w!=-1 && trans[w][c]==x){
trans[w][c]=y;
w=slink[w];
}
minlen[y]=maxlen[slink[y]]+1;
return z;
}
}sam;
int m;
ll ans;
int main(){
// freopen("hihocoder1445.in","r",stdin);
// freopen("hihocoder1445.out","w",stdout);
char T[MAXL+10];
scanf("%s",s);
len=strlen(s);
int U=sam.add_char(0,-1,0);
for(int i=0;i<len;++i){
U=sam.add_char(s[i],U,i);
}
for(int i=1;i<sam.n;++i){
ans+=(ll)(sam.maxlen[i]-sam.minlen[i]+1);
}
cout<<ans<<endl;
return 0;
}

總結

以上是生活随笔為你收集整理的【后缀自动机】hihocoder1445 后缀自动机二&#183;重复旋律5的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。