給你一個長度爲\(n\)的字符串\(S\)。html
有\(m\)個操做,保證\(m≤n\)。ios
你還有一個字符串\(T\),剛開始爲空。數組
共有兩種操做。測試
第一種操做:優化
在字符串\(T\)的末尾加上一個字符。spa
第二種操做:code
在字符串\(T\)的開頭加上一個字符。htm
每次操做完成後要求輸出有幾個\(l∈[1,T.size]\)知足如下條件:blog
對於\(∀i∈[1,l]\)有\(T_{T.size−l+i}≠S_i\)隊列
\(Tip:\)字符串下標從\(1\)開始。\(T.size\)表示\(T\)的長度。
第一行兩個正整數\(n,m\)。
第二行\(n\)個正整數,用空格隔開,第\(i\)個整數表示\(S_i\)。
接下來\(m\)行,每行兩個數字 \(opt,ch\),\(opt=0\)表示在\(T\)的末尾加一個字符\(ch\),\(opt=1\)表示在\(T\)的開頭加一個字符\(ch\)。
共\(m\)行,每行一個非負整數表示第\(m\)操做後的輸出。
輸入 #1
10 3 1 2 3 1 2 3 2 3 2 3 0 1 1 2 0 3
輸出 #1
0 1 1
注意:本題採用捆綁測試,只有當你經過一個\(subtask\)的全部點後,你才能拿到這個\(subtask\)的分數
對於全部的數據 \(n≤10^6,m≤3.3333×10^4,|∑|≤10^3,S_i∈[1,|∑|]。(∑表示字符集)\)
\(subtask1(17\%):m≤333\)
\(subtask2(33\%):m≤3333\)
\(subtask3(20\%):|∑|≤2∣\)
\(subtask4(30\%):\)無特殊條件
第一次操做後,\(T=「1」\),
\(l=1\)時\(T[1]=S[1]\),因此答案爲\(0\)
第二次操做後,\(T=「21」\),
\(l=1\)時,\(T[2]=S[1]\)
\(l=2\)時,\(T[1]!=S[1]\),\(T[2]!=S[2]\),因此答案爲\(1\)
第三次操做後,\(T=「213」\),
\(l=1\)時,\(T[3]!=S[1]\);
\(l=2\)時,\(T[2]=S[1]\);
\(l=3\)時,\(T[3]=S[3]\),因此答案爲\(1\)
\(O(m^3)\)的作法很容易想,按照題意模擬便可。
預計得分\(17pts\)。
對於\(O(m^2)\)的作法,由於這個題其實是查找\(S\)的前\(l\)個和\(T\)的後\(l\)個是否每一個都不相等,咱們考慮記錄\(dp[l]\)表示在上述意義下\(l\)是否合法(\(0\)表示都不相等,\(1\)表示至少有一個等)。容易知道,在\(T\)串最後插入一個字符時,由於\(S\)串始終不變,\(T\)串的最後\(l\)個字符從本來\(T\)串的後\(l\)個字符變成了本來\(T\)串的後\(l−1\)個字符加上新加入的字符,因此爲了比較新的\(T\)串後\(l\)個字符是否合法,咱們只須要比較新字符、本來\(T\)串的後\(l−1\)個字符是否有相等便可。即\(dp[i]=dp[i−1]|(ch==S[i])\)。這樣,對於每一個加入的字符,只須要用\(O(1)\)的複雜度檢查每一個枚舉到的\(l\)是否合法便可。
在\(T\)串最前面插入一個字符時,由於本來的\(i\)個後綴依然沒有變化,只是增長了一個新的後綴\(i+1\),因此咱們只需暴力\(check\)新加入的後綴,對於每一位枚舉是否有相等的便可。
時間複雜度\(O(m^2)\),預計得分\(50pts\)。
考慮優化\(O(m^2)\)的作法,咱們找到了狀壓神器——\(bitset\),它能夠將複雜度優化到原來 \(\dfrac{1}{w}\)(\(w\)爲計算機字長,通常爲\(32\)或\(64\))。若是常數優秀一些這個方法能夠過。
考慮剛纔的方法算過了哪些不可能合法的狀態,咱們知道全部的字符其存在位置都是獨立的,因此咱們用\(|∑|\)個\(bitset\)數組\(ch[x]\)記錄字符\(x\)在\(S\)中的哪些位置上出現過。只要加入的新字符\(x\)對應的位置是\(ch[x]\)上\(1\)的位置,則該狀態確定不合法。
因此這樣優化的關鍵在於同時算出了全部合法的狀態。因此咱們用\(dp\)的第\(i\)位的\(0/1\)表示後綴長度爲\(i\)時是否有相等的字符。
若是在\(T\)串尾部加入新的字符,則對於長度是\(i\)的狀況必定是由\(i−1\)的狀況和新加入位的狀況同時轉移來(見上述\(O(m^2)\)作法),而全部新加入的位對應與\(S\)串中哪些位相同已經存儲好,假設加入的字符是\(x\),則\(dp=(dp<<1)|ch[x]\)。
若是在\(T\)串頭部加入新的字符,設原來\(T\)串有效的後綴長度有\(l\)位,則新的\(T\)串後\(l\)位是否合法狀態不變,因此新舊\(T\)串前面\(l\)位答案同樣;
在\(T\)串頭部插入新字符時,咱們將問題拆成兩部分:
第一,咱們發如今頭部加入字符時,後面的全部字符都日後移了一位。
第二,咱們須要比較加入的新字符和第一個字符是否相同。
很明顯困難在於解決第一個問題。由於咱們若是要想比較移動以後的字符和\(S\)的關係,在不知道其它任何東西的狀況下,須要另用一個\(O(m)\)檢查。
解決這個問題的方法是一個很是重要的思想:費用提早。在每次加入一個字符時,咱們將這個字符所能貢獻的答案都記在\(dp\)中。方法很簡單,假設咱們每次加進的字符是\(x\),考慮這一位對應到\(S\)串的全部可能。若是\(x\)對應到的某一位上\(ch[x]\)在一樣的位上剛好是\(1\),說明加入字符使當前這個\(x\)剛好對應到剛纔的那一位上,則這樣的方案確定是不合法的。
考慮如何進行這樣的操做。假設\(x\)是在第\(i\)位加入隊列而且對應的\(ch\)值在第\(k\)位上是\(1\),\(dp[i-1+k]\)必定不合法(假設沒有從後插入的)。這時\(x\)距離隊尾的距離是\(i−1\),因此當\(x\)取到\(k\)時,隊尾應該到\(k+(i-1)\)位,可是注意在\(bitset\)裏是反着存的,因此:
\(dp=dp|(ch[x]<<i−1)\)
時間複雜度爲\(O(\dfrac{m^2}{w})\)
代碼實現:
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<ctime> #include<climits> #include<algorithm> #include<bitset> using namespace std; const int M=40000; bitset<M> ch[1010],dp,limit; int n,m; int main() { int i,t,x; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) scanf("%d",&x),ch[x].set(i); for(;i<=n;i++) scanf("%d",&x); limit.set(); for(i=1;i<=m;i++) { scanf("%d%d",&t,&x); limit.reset(i); if(t==0) dp=(dp<<1)|ch[x]; else dp=dp|(ch[x]<<i-1); printf("%d\n",(~(dp|limit)).count()); } return ~~(0-0); }