對 SAM 和 PAM 的一點理解

2022-06-28 06:24:10 字數 2245 閱讀 5663

感覺自己學 sam 的時候總有一種似懂非懂、雲裡霧裡、囫圇吞棗、不求甚解的感覺,是時候來加深一下對確定性有限狀態自動機的理解了。

從 sam 的定義上理解:sam 可以看作一種加強版的 trie,它可以高度壓縮乙個字串的子串資訊,一條從根出發到終止結點的路徑對應了原串的乙個字尾,而任意乙個從根出發的路徑對應了原串乙個子串。子串和從根出發的路徑一一對應。在這種的理解下,每乙個結點的含義並不是固定的,它到底對應哪個子串取決於那條路徑是怎麼到達它的;而邊有著確定的含義。

從 parent tree 的角度去理解連邊的含義:顯然等價類的個數是 \(\theta(n)\) 級別的,並且兩個不同等價類的 \(\texttt\) 集合要麼無交集,要麼相包含,因此可以建出乙個由 \(\texttt\) 集合的包含關係鏈結而成的樹——parent tree,它的連邊——字尾鏈結,若是向下看,是在乙個等價類的前面加上乙個字元,從而分成若干的其他等價類;向上看,它是指向包含當前集合的最小的集合;而字尾自動機的連邊是在乙個等價類的後面加上乙個字母,看看它會指向誰,顯然對於同乙個添上的字母,這個指向是唯一確定的。

從結點的含義去理解:雖然乙個點它並不一定對應哪乙個子串,但是它對應的若干子串一定有乙個共同的 \(\texttt\),也就是說,若長的那個存在了,短的那些一定是它的子串;短的出現了,那麼它向前一些一定是那些長的。由於上面的結論,"本質"不同的子串的個數是線性的,因此我們建立這樣乙個自動機,其中的每乙個結點都對應了一種子串。這也是為什麼 parent tree 的結點與 sam 的結點一一對應。

關鍵在於上面的這些理解是同時成立的,可以把 parent tree 上獲得的資訊用作 sam 資訊的補充;兩者相得益彰,互相成就。

來看一道例題:p3975 [tjoi2015]弦論,大意是多次詢問乙個串的字典序第 \(k\) 小子串,分為要求本質不同和不要求本質不同的兩種。

顯然我們只需要使用類似二叉查詢樹的那種方法就行了,因此問題轉化為了求乙個結點後面有多少種子串;如果要求本質不同,那麼就在自動機上拓撲 dp 一下就行了,因為自動機會自動合併本質相同的子串;如果不要求呢?那麼就需要結合第三種理解,我可以在字尾樹上求出每個節點對應的子樹內葉子節點出現次數的總和,顯然這個就是這個結點的出現次數——乙個等價類的出現次數等於它的孩子們出現次數的和,而葉子節點的出現次數一定是和 sam 一起建出來的。而乙個結點的後面的出現次數 \(f_x = sze_x + \sum\limits_f_t\),這個由加法原理得到。這就引出了乙個點的 \(\texttt\) 集合大小的另乙個含義——若從根遍歷到這個結點的路徑,它究竟有多少種結束方式

main():

for (int i = 1; i <= cnt; i++) c[len[i]]++;

for (int i = 1; i <= cnt; i++) c[i] += c[i - 1];

for (int i = 1; i <= cnt; i++) a[c[len[i]]--] = i;

for (int i = cnt; i; i--) if (t)

sze[fa[a[i]]] += sze[a[i]]; else sze[a[i]] = 1;

sze[1] = 0; for (int i = cnt; i; i--)

這裡提到一件爭議: p2336 [scoi2012]喵星球上的點名 這題 @fighter_oi 的題解下由兩位金鉤大佬提出了意見——乙個說這個可以被卡到 \(n\sqrt n\),乙個說這個可以優化到 \(n\log n\);事實上,我認為這個已經是線性的了,不存在什麼 \(n\sqrt n\) 什麼 \(\log\) 的問題。我們看這樣的**:

inline void update1(int x, int y)

inline void build()

雖然這樣的**看似是在暴力,看似可以被卡,但是實際上,它只是補充不漏地遍歷了每個子串的所有「本質」不同子串,「不同字串」 的個數又是線性的,所以總的複雜度是 \(\sum|s_i|\),依然是線性的。甚至改成這樣也一樣能過:

inline void update1(int x, int y)

事實證明,對於比較難以維護的資訊,我們完全可以遍歷乙個串的每個「子串」來暴力新增上資訊,這麼做的複雜度依然是和輸入量呈線性的。

upd:下面的這種寫法是錯誤的,是 \(o(\frac 14 n^2)\) 級別的(用000000001111111這樣的串卡掉),而前面的寫法是線性的。

對equals和 的一點理解

概述 簡單的來說,equals是比較內容,是比較位址值 詳細 記憶體可以分為堆記憶體和棧記憶體,簡單的理解一般棧中主要存放一些基本型別的變數 int,short,long,byte,float,double,boolean,char 和物件控制代碼 物件位址值 而堆記憶體一般存放具體的物件控制代碼內...

對 threadfence的一點理解

一直沒搞清楚,cuda 2.2版增加的 threadfence到底有何作用,直到今天看到sdk 3.0手冊 中的下面例子才恍然大悟.中文為我的理解,嘿嘿 乙個求和的例子 device unsigned int count 0 統計有幾個block結束的變數 shared bool islastblo...

對GBDT的一點理解

gbdt由一系列的回歸樹組成,如下圖所示 樹的深度未必都要一樣,下圖僅為示意圖 gbdt原理 針對每乙個類別訓練一系列的回歸樹,再累加每個類別回歸樹的 值得到針對每個類別的最終的 值。單獨拿乙個類別來說,訓練的過程中假設需要 的值為f xi 實際的值為yi 有loss function l yi,f...