【IT之家開箱】小米 11 青春活力版圖賞:晴光白雪,輕薄出眾
字尾自動機上儲存著一個串的所有後綴,由於所有子串都能通過新增字元變為字尾,所以所有子串也都儲存在後綴自動機中
字尾自動機之所以能壓縮狀態,是因為把所有 \(endpos\) 集合相同的串歸為一個狀態
定義 \(endpos(S)\) 指串 \(S\) 在母串中所有出現時結尾位置的集合,以 \(abaa\) 這個串為例:
\(a:1,3,4\)
\(ab,b:2\)
\(aba,ba:3\)
\(abaa,baa,aa:4\)
對於每一類 \(endpos\) 的字串,都歸入一個節點
\(SAM\) 的 \(fail\) 樹上的父親,是包含這個節點 \(endpos\) 集合的最小集合
有這樣的性質:所有節點兒子的集合沒有交集
對於一個 \(endpos\) 集合,如果給出一個長度 \(len\),那麼就可以唯一確定出一個子串
而對於每個集合長度 \(len\) 都有一定的限制,設為 \(minlen,maxlen\)
有這樣的性質:\(minlen(p)=maxlen(fa(p))+1\)
而 \(SAM\) 本質上是一個 \(DAG\),有向邊代表字元的轉移
有這樣的性質:\(endpos(son[p][w])\subseteq endpos(fa[p])\)
利用以上性質可以證明 \(SAM\) 的點數與邊數規模都為 \(O(n)\)
對於自動機的構建,目前來看似乎需要改動板子的情形不多,大部分情況直接理解性默寫即可
void insert(int w){ int p=last,np=last=++tot; siz[tot]=1;len[np]=len[p]+1; for(;p&&!son[p][w];p=fa[p])son[p][w]=np; if(!p)fa[np]=1; else{ int q=son[p][w]; if(len[q]==len[p]+1)fa[np]=q; else{ int nq=++tot; fa[nq]=fa[q];for(int i=0;i<26;i++)son[nq][i]=son[q][i]; fa[q]=fa[np]=nq;len[nq]=len[p]+1; for(;p&&son[p][w]==q;p=fa[p])son[p][w]=nq; } } return ; }
首先 \(np\) 是代表最後一個字尾的節點,整個過程一個主要目的是為了給其找父親
順著 \(endpos\) 是最後一個點的鏈往上爬直到有 \(w\) 這個兒子
如果沒有,直接認 \(1\) 為兒子,結束
至於另外的情況如果不滿足長度條件會產生的影響,目前還沒有看懂,總之應該把 \(q\) 分裂成兩個節點
首先根據本質字尾自動機上儲存了這個串中的所有子串,並且每個節點上的子串各不相同
自動機相當於一張 \(DAG\),從起點出發到任意一點結束走過的一條路徑唯一對應了一個本質不同的子串
那麼考慮 \(DAG\) 上的 \(dp\),\(f[u]=1+\sum f[son]\),注意減去空串的 \(1\)
另一種理解方式是對於每個 \(endpos\) 表示了一類子串
一個節點儲存的是 \([minlen,maxlen]\) 的子串
而 \(minlen=len_{fa}+1\)
一個子串出現的個數就是當前節點儲存的 \(endpos\) 集合的大小
那麼集合大小可以由 \(parent\) 樹上的子節點資訊得來
即 \(siz[u]=\sum siz[son]\)
由於建出真實的圖比較慢,可以按照 \(len\) 排序而得出父子關係,由於其小於 \(n\),可以進行基數排序
每次增加一個字元的時候動態維護自動機上的答案
為 \(len[np]-len[fa[np]]\)
一個子串相當於從起點出發的一條路徑
那麼考慮 \(dp\) 出走當前節點的所有出邊將會產生有多少個子串
那麼直接圖上 \(dp\) \(f[u]=\sum f[son]\) 即可
考慮兩個串的最長公共子串應該怎樣求
可以類比 \(AC\) 自動機,它的失配指標相當於指向最長公共字尾,而後綴自動機則儲存了所有子串,所以是指向最長公共子串
類似與 \(AC\) 自動機,用一個串在另一個串的 \(SAM\) 上跑,若失配則條父親,跑出來就是當前結尾的最長公共子串
如果要處理多個子串,那麼把剩餘的都在第一個上跑一邊,對於每個位置分別取 \(min\) 即可
仍然是匹配的模式,考慮迴圈同構的處理方法
相當於長度擴充套件一倍,每次要求長度小於 \(len\)
由於本質不同的只算一次,那麼字尾自動機上的每個節點打時間戳去重即可
這次多了區間的限制
考慮還是跑出長度 \(f[i]\),那麼對於區間 \([l,r]\) 相當於是查詢 \(max\{min(f[i],i-l+1)\}\)
發現內層多了個 \(min\) 不好處理,由於 \(f[i]\) 每次加一具有單調性,那麼可以先二分答案,使得每一個都不受限制,然後用 \(ST\) 表查詢區間最大值
考慮沒有區間限制的暴力做法:對 \(S\) 的 \(SAM\) 跑 \(T\) 進行匹配,跑到第一個沒有匹配項的地方找到比 \(T\) 大的轉移,如果都能匹配上,那麼往回退,直到有一個滿足條件的
現在有了區間的限制,如果能判斷後綴自動機上的這個節點的 \(endpos\) 集合中有沒有當前區間內的點就可以正常做了,那麼直接在 \(parent\) 樹上線段樹合併維護 \(endpos\) 集合即可