求長度為\(\text\)的包含給定連續子串\(\text\)的 0/1 串的個數。(\(|t|<=15\))
通常來說這種題目應該立刻聯想到狀壓 dp 與取反集——這樣就不用考慮大量重複情況的容斥問題。設\(f_\)表示前\(i\)個字元、最後\(|t|\)個字元為\(s\)、不包含給定連續子串的情況數,狀態轉移方程簡單不述。時間複雜度 \(\theta(2^\text)\)。
上述演算法的時間複雜度相當高昂,當\(\text\)十分大或者不是乙個 0/1 串而是乙個字串的時候完全不可做,並且大量的狀態是無用的,因為我只關心不出現給定的字串,然而相當多的狀態不用轉移已經注定了不可能出現給定字串,用狀態壓縮表示表示整段的完整狀態非常虧本。重磅的優化來了:
我們在 dp 狀態的設定上模擬 kmp 演算法的過程。設\(f_\)表示前\(i\)個字母、已經匹配到了模式串的\(j\)時有多少種情況。那麼狀態轉移也依照 kmp 演算法:列舉當前點的\(j\)並列舉下乙個點填的字元,在失配樹上找到下乙個點的\(j\),把\(f\)累加過去,時間複雜度\(\theta(\text|t|^2s)\),其中\(s\)是字元種類數。
當然不止可以在模式串上加強,還可以在文字串上加強:當\(\text\)是天文數字的時候,我們完全可以用矩陣快速冪優化上述過程,時間複雜度是\(log\)的。
反集轉換肯定是要想到的,但是到了這一步後狀態設定完全無法下手,三段的序列和你設什麼狀態都不是。注意到三段和的大小都非常小,連續的這麼三段的長度的總數都非常少,總數只有\(2^\)個。那麼我們這些模式串全部列舉出來,問題就轉化為不出現這些字串的字串的個數。把模式串丟進乙個 trie 裡面建出 ac 自動機,然後把每個終止點在失配樹中的子樹的結點都打上標記——不可轉移,然後就跟上題一模一樣了。
考慮這東西有了兩列怎麼辦。我們使用一種巧妙的輪廓線 dp:設\(f_\)表示到了點\((i,j)\)、輪廓線上的狀態是\(\text\)、當前行與模式串的第一行匹配到\(x\)、與第二行匹配到\(y\)時的方案數。輪廓線的狀態由 0/1 組成,表示它的字尾是否能夠與模式串的第一行完全匹配。狀態轉移方程顯然,前兩維可以滾動掉,後面的維度需要大量的 memset (大概二十億?),所以還是會超時。解決辦法是把所有變數和陣列都放進 register,這樣刷表的速度就可以上天。這種解法就是這題目前的最優解法了。
處理包含一種模式串的字串總數,應該轉化為求不包含它的字串數,這個問題可以用 kmp 的思想大大優化。
一類巧妙利用利用分治的序列求值
這個方法經常用於這類問題 給定序列 a 並定義基於序列 a 的函式 f l,r 求 sum limits f l,r 這個方法的核心做用是 將不滿足可減性的求值,變成只需要可以區間拼合 即滿足區間可加性 的求值式子。拆成每個點的貢獻處理。從左到右推進端點 r 開乙個資料結構維護對於當前每個 l 的函...
一類利用佇列優化的DP
這是乙個 o n 2 的狀態和轉移方程 f i,j left f i 1,j 1 k 1 leq j max j 1 end right.這個方程目測是 theta n 2 的,但是實際上,上面的那個方程只是把陣列整體位移了,下面的方程只是在位移後的陣列開端添上了乙個數,這個完全可以通過佇列來實現,...
一類有關序列的技巧問題
問題一 我們有乙個數列a1,a2.an,你如今要求改動數量最少的元素,使得這個數列嚴格遞增。當中不管是改動前還是改動後。每乙個元素都必須是整數。請輸出最少須要改動多少個元素。選取最長的符合要求的序列,然後把其它值改變就可以。怎樣找到符合要求的序列?由於要遞增,所以每乙個數和它自身所在的位置有關,也就...