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

Sample Output

【样例输出 1】
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 }

 

posted @ 2018-09-18 21:20  Unstoppable728  阅读(245)  评论(0编辑  收藏  举报