BZOJ3620: 似乎在梦中见过的样子(KMP)
Description
“Madoka,不要相信 QB!”伴随着 Homura 的失望地喊叫,Madoka 与 QB 签订了契约.
这是 Modoka 的一个噩梦,也同时是上个轮回中所发生的事.为了使这一次 Madoka 不再与 QB签订契约,Homura 决定在刚到学校的第一天就解决 QB.然而,QB 也是有许多替身的(但在第八话中的剧情显示它也有可能是无限重生的),不过,意志坚定的 Homura 是不会放弃的——她决定
消灭所有可能是 QB 的东西.现在,她已感受到附近的状态,并且把它转化为一个长度为 n 的字符串交给了学 OI 的你.
现在你从她的话中知道 , 所有形似于 A+B+A 的字串都是 QB 或它的替身 , 且len(A)>=k,len(B)>=1 (位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串),然后你必须尽快告诉 Homura 这个答案——QB 以及它的替身的数量.
Input
第一行一个字符串,第二行一个数 k
Output
仅一行一个数 ans,表示 QB 以及它的替身的数量
Sample Input
【样例输入 1】
aaaaa
1
【样例输入 2】
abcabcabc
2
aaaaa
1
【样例输入 2】
abcabcabc
2
Sample Output
【样例输出 1】
6
【样例输出 2】
8
6
【样例输出 2】
8
解题思路:
首先一上来想到的是:给你一个字符串,怎么快速地判断是不是ABA式的。
可以看出对于字符串匹配的算法(hash/KMP)
hash在这个问题上是远远劣于KMP的:
hash的暴力枚举时间复杂度已经不可承受,而且似乎没有方法优化的样子。
KMP有一个优秀的性质:你可以很快的知道一个字符串前缀后缀重复的情况,而这恰恰就是ABA式的字符串。
简单来说假如说给定起点字符串的起点KMP求next数组的时候判断其小于k的最小前缀,存在则ans++
那么每次暴力枚举字符串起始点进行如上算法,输出答案就好了,注意前缀的next也是合法情况,开数组记录最小值就好了。
时间复杂度O(n2/2),强调/2是有原因的n<=15000
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using std::min; 5 char ch[1000000]; 6 int nxt[1000000]; 7 int mini[1000000]; 8 int ans; 9 int n,m,k; 10 void Kmp(char *a) 11 { 12 nxt[0]=nxt[1]=0; 13 mini[0]=0x3f3f3f3f; 14 int len=strlen(a+1); 15 for(int i=2,j=0;i<=len;i++) 16 { 17 while(j&&a[i]!=a[j+1]) 18 j=nxt[j]; 19 if(a[i]==a[j+1]) 20 j++; 21 nxt[i]=j; 22 if(j<k) 23 mini[j]=0x3f3f3f3f; 24 else 25 mini[j]=min(mini[nxt[j]],j); 26 if(mini[j]*2<i) 27 ans++; 28 } 29 } 30 int main() 31 { 32 scanf("%s",ch+1); 33 scanf("%d",&k); 34 int lenl=strlen(ch+1); 35 for(int i=0;i<lenl;i++) 36 Kmp(ch+i); 37 printf("%d\n",ans); 38 return 0; 39 }