吐槽:前两天打组队赛遇到一个字符串的题考了这个(见:http://acm.hdu.edu.cn/showproblem.php?pid=5972 )
当时写了个KMP瞎搞然后TLE了(害),赛后去查了许多资料似乎就看见一个题考了这么个鬼东西…
目录
t[i]==t[j+1]
改成包含关系,即我们要让\(T[1,j+1]\)这个前缀包含\(T[i-j,i]\)这个后缀如果\(k_i\)小的话这一步仍然可以认为是\(O(1)\)的时间,整个处理过程仍然是\(O(|T|)\)的。嗯到这里感觉都不错,接下来进行两个字符串的匹配了,\(S\)和\(T\)的比较也改成比较是否包含。如果失配的话同样是\(j=nxt[j]\)地往回跳,一直到\(j==|T|\)匹配成功…到这里似乎都没什么问题和其他算法问题一样,我们可以考虑换一个维护的对象。下标,字符集…
比如这里,我们考虑从另一个角度切入字符串匹配的问题:对于字符集比较小的匹配,对模式串\(T\)里每个字符出现的位置进行记录:即用一个数组\(B[i][j]\)表示字符\(i\)在第\(j\)个位置是否出现。这样记录能够处理这题里令我们头疼的问题:模式串的一个位置允许多种取值。
好了现在有了这么一个想法,先试试看最暴力地要怎么做这个问题
(约定|S|=m,|T|=n)
\(O(n)\)地求出\(B[][]\)数组
for(int i=1;i<=n;i++)
int k;scanf("%d",&k);
for(int j=1;j<=k;j++)
int t;scanf("%d",&t);
B[t][i]=1
每次暴力匹配\(S\)和\(T\),时间复杂度还是\(O(nm)\)
for(int i=1;i<=m;i++)
int j=1;
for(;j<=n&&B[s[i+j-1]][j];j++);
if(j==n+1)
match!
和其他字符串算法的思路一样,我们尝试能不能通过维护一些前后缀的信息来减少信息的冗余:比如这里我们发现,上面的算法每次都在暴力\(O(n)\)地比较\(S[i,i+n-1]\)和\(T[1,n]\),我们可以把这个过程看成\(S[1,i+n-1]\)的后缀和\(T[1,n]\)的前缀进行比较,于是类似KMP的思路,也许我们可以去维护\(S\)的后缀和\(T\)的前缀相关的信息!(这就是Shift-And算法的思路!)
我们考虑再用一个数组\(D[]\)来维护这样一个信息:\(D[j]=1\)当且仅当\(S[i-j+1,i]\)和\(T[1,j]\)匹配,即\(S\)的一个后缀是\(T\)的前缀。否则\(D[j]=0\)。马上我们将会发现用Bool类型储存这样一个信息的优越性。
如果我们让\(i,j\)两个指针一起跑(如图),能写出递推式:\(D[j+1]=(D[j])\&(S[i+1]==T[j+1])\)。进一步我们利用前面做好的数组\(B[][]\),可以把相等的判定修改一下,变成:\(D[j+1]=D[j]\&B[S[i+1]][j+1]\)
到这里都还只是逐位地进行位运算的比较,但是我们注意到这个\(D[]\)似乎可以做成一个\(bitset\),把它看成是一个长度为\(|T|\)的二进制数的话,尝试直接用一个\(D\)表示这个数组,用位运算来实现这个递推。
考虑上面的过程,从\(D[j]\)到\(D[j+1]\)需要先把上一位\(D[j]\)的信息复制过来,再对\(j+1\)位进行一个取\(\&\)的操作,考虑从\(i=1,j=1\)往上递推的整个过程…对于每个\(i\),每次遍历\(1,2,3,…,j…,|T|\),复制信息…对应位置取\(\&\),这个复制信息的过程不就相当于把一个二进制数全部左移一位么?每次取\(\&\)也很麻烦,我们把\(B[i][j]\)的第二个维度也压掉,直接对两个二进制数按位\(\&\),同时为了保证\(\&\)正确性,每次左移完了之后把最低位赋为1。
另外,对于超过\(|T|\)的\(j\)的信息我们可以直接丢掉,所以也不用担心丢失什么信息。
至此,我们已经可以抛去\(j\)这个指针,得到从\(i\)到\(i+1\)递推式:
\(D=(D<<1|1)\&B[S[i+1]]\)实现
const int N=5000005;
const int M=1005;
char s[N];
char t[M];
bitset<M>B[10],D;
int n,len;
int main(){
scanf("%d",&n);
rep(i,1,n){
int k;scanf("%d",&k);
rep(j,1,k){
int t;scanf("%d",&t);
B[t].set(i,1);
}
}
scanf("%s",s+1);
len=strlen(s+1);
rep(i,1,len){
D=(D<<1).set(1)&B[s[i]-'0'];
if(D[n]){
char ch=s[i+1];
s[i+1]=0;
puts(s+i-n+1);
s[i+1]=ch;
}
}
return 0;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章