10w长度的A字符串,若干个总长10w的B[]字符串。
求属于A但不属于任一个B的不同子串个数。
看到题目就可以估计出大概用后缀数组可以搞。
想象一下对一个字符串S用后缀数组预处理一下,height[i]就是第i名和第i-1名字符串的最长公共前缀,那么第i名的字符串前height[i]个前缀串(abaa的前3个前缀串分别是:a、ab、aba)在之前几名的字符串中都出现过,而剩下的几个必定没出现过(否则公共前缀就可以更长了)。每次把没出现过的前缀串个数加起来就是字符串S的不同子串个数。
那这道题目里不但要去掉跟自身A串重复的,还要去掉与B[]字符串重复的。
可以试着把A、B[]字符串都用一个特殊字符连接起来,后缀数组预处理一下,然后在height数组中观察每一个A串的后缀串:
1、假设这个后缀串是以A[i]开头,A的长度为L,那么我们可以说A串中以A[i]开头的子串有L-i个,对于每个A[i]我们现在就只考虑这L-i个;
2、我们要去掉与本身重复的串,由于只要找之前被计算过的那些,所以从这一名开始往上找,找到第一个A的后缀,求一下最长公共前缀,记为A_lcp;
3、我们还要去掉与B[i]重复的串,朴素思想就是找出max{此后缀串与Bx串的最长公共前缀 | Bx是B[]的所有后缀},所以往上往下各找一个B[]的后缀,也求出最长公共前缀,分别记为B_up_lcp,B_down_lcp;
所以要去掉的字符串个数就是f_max(A_lcp,B_up_lcp,B_down_lcp);这里要注意的是求最长公共前缀时分割用的特殊符号也被看做串的一部分,所以这里得到的值可能超过A[i]开头的字符串个数(L-i),两者取小即可。
另外一个小方便是求A_lcp和B_up_lcp时,一个往上找A串,一个往上找非A串,然后还要求个lcp的较大值,所以这个值就是height[i]了。
求B_down_lcp时,只要维护当前观察串之后的第一个非A串名次即可,每次用RMQ方法求一下。
TLE无数次,因为用strcat把B[]连接起来………………………………
核心代码:
ll cal_ans(){ makesa();lcp();//后缀数组预处理,得到sa、rank、height数组 get_rmq(height,1,N);//对height数组作RMQ的预处理 int i,NOTA=0; ll res=0; int temp; for(i=1;i<=N;i++){//从第1名观察到第N名 if(sa[i]>=L)continue;//如果第i名的串不是A的后缀串那就这么continue了 while(NOTA<=N&&(NOTA<=i||sa[NOTA]<L))NOTA++;//往后找第一个非A串的名次 //temp保存当前串与(之前处理过的A的后缀串 and 所有的B[])的lca,即要减去的串的个数 if(NOTA<=N)temp=f_max(height[i],query_min(i+1,NOTA));//如果后面有非A串,max(height[i],B_down_lca); else temp=height[i];//否则就是height[i]; res+=f_max(0,(L-sa[i])-temp);//L-sa[i]是这个后缀的总长度也是这里面包含的子串个数 //如果要减去的串中包含进了分割用的特殊字符就可能过长 } return res; }