给你一个长度为\(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); }