字尾自動機學習筆記2(hiho128周)

2021-07-25 09:22:25 字數 3524 閱讀 6297

字尾自動機最出名的應該是其時空複雜度均為o(length(s)),想要實現o(length(s))的構造,我們對於每個狀態不能儲存太多資料。例如substring(st)肯定是沒法儲存下來了。對於狀態st我們只儲存如下資料:

maxlen[st]:st包含的最長子串的長度

minlen[st]:st包含的最短字串的長度

trans[st][c]:st的轉移函式,c為新的字元

slink[st]:st的suffix link

suffix-path(u->s)是s[1..i], s[2..i], s[3..i], ... , s[i], ""(空串)對應的狀態們恰好就是從u到初始狀態s的由suffix link連線起來路徑上的所有狀態(s[1..i]對應的狀態是u)。例如suffix-path(7->s)=,suffix-path(9->s)=。

下一步說下轉化函式怎麼實現的,主要分為三種情況分析

s[1..i+1]這個子串不能被以前的sam識別,所以我們至少需要新增乙個狀態z,z至少包含s[1..i+1]這個子串。例如下面的s[1..i]="aa",那麼s[1..i]="aab",新增了乙個新的狀態3。

最簡單的情況:對於suffix-path(u->s)的任意狀態v,都有trans[v][s[i+1]]=null。這時我們只要令trans[v][s[i+1]]=z,並且令slink[st]=s即可

這裡trans[v][i+1]=null的意思是,例如對下面的情況中suffix-path(u->s)的狀態都有2,1,s,對於狀態2包含的字串有,trans[2][「b」]得到的字串為在之前的字串中沒有出現過所以trans[2][b]=null,同理trans[1][「b」]=null,trans[s][「b」]=null,這是只需要使trans[2][「b」]=trans[1][「b」]=trans[s][「b」]=3(圖中紅色實線),同時slink[st]=s(圖中紅色虛線)。

對於有trans[v][s[i+1]]!=null的情況,如下圖,當新增新的字元"a"的時候,對於狀態s有trans[s]["a"]=,已經在狀態1中出現。對於這種情況分兩種討論。

可以認為在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(圖中紅色虛線)即可。

第二種:如果x中包含的最長子串 不是 v中包含的最長子串接上字元s[i+1],等價於maxlen(v)+1 < maxlen(x)

例如下面這種情況:對於狀態3存在trans[s][「b」]=3(即trans[s][b]中的字串為"b"存在於substrings(3)中),且longest(3)="aab",longest(s)+'b'="b",兩者不相等。這種情況需要從x拆分出新的狀態y,並且把原來x中長度小於等於longest(v)+c的子串分給y,其餘字串留給x。同時令trans[v..w][c]=y,slink[y]=slink[x], slink[x]=slink[z]=y。例如下圖新增加了乙個狀態5,同時將原來指向狀態3中長度小於等於longest(s)+"b"的子串「b」指向狀態5,同時令trans[s]["b"]=5,slink[5]=slink[3]=s, slink[3]=slink[4]=5。

問題

小hi平時的一大興趣愛好就是演奏鋼琴。我們知道乙個**旋律被表示為一段數構成的數列。

現在小hi想知道一部作品中出現了多少不同的旋律?

解題方法提示

輸入

共一行,包含乙個由小寫字母構成的字串。字串長度不超過 1000000。

輸出

一行乙個整數,表示答案。

樣例輸入

aab
樣例輸出

5
**

//

// main.cpp

// hiho128

//// created by 小哲 on 16/12/13.

//#include #include#include using namespace std;

const int maxl = 1000000;

int n = 0;

int maxlen[2 * maxl], minlen[2 * maxl], trans[2 * maxl][26], slink[2 * maxl];//長度為2 * maxl是因為這裡的每新增乙個新的字元最多增加兩個狀態(乙個狀態z,乙個中間狀態y)

int new_state(int _maxlen, int _minlen, int* _trans, int _slink)

slink[n] = _slink;

return n++;

}int add_char(char ch, int u)

if(v == -1)

int x = trans[v][c];

if(maxlen[v] + 1 == maxlen[x])

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)

minlen[y] = maxlen[slink[y]] + 1;

return z;

}int main(int argc, const char * argv)

}cout<

學習筆記 字尾自動機

其實我字尾自動機在 2020 2 的時候就會了,刷了很多題,但是一直沒有搞懂原理,現在補一發關於字尾自動機原理的部落格。我盡量節約時間,把最重要的東西寫的盡量好理解。我是看這位巨佬的部落格學的,所以會有一些重合的地方,但是我會按照我自己的理解寫 仔細看你會發現我們的表述相差很大 爭取達到乙個優化的效...

字尾自動機 筆記

參考了hihocoder和clj的課件,看了看hzwer的 懂了些東西,記一下。字尾自動機是一棵trie樹。給出乙個字串s,對於s的乙個子串s,right s 代表乙個集合,為s在s中所有出現的結束位置集合。以s aabbabd 為例,right ab 因為 ab 一共出現了2次,結束位置分別是3和...

字尾自動機學習

今天終於把這週的坑填了,同樣看了很多部落格,這裡就不詳細總結了,就簡單整理一下了。應用 1 存在性查詢 給定文字t,詢問格式如下 給定字串p,問p是否是t的子串。直接按著路徑走,看是否存在即可 2 不同的子串個數 對於每乙個節點即為 len i len fa i 加和即可 3 不同子串的總長 這裡我...