Bzoj 3620---似乎在梦中见过的样子(KMP)

题目背景第二行madoka打错了???

KMP暴力啦~~~~

我们使用 KMP 的 P 数组来找这个前缀,直接从 P[r] 向前找最坏情况会导致变成 O(n^3) ,所以我们应该优化一下:

P 数组如果视为 Father 数组,那么它是一棵树。我们求的就是一个节点到根的路径上有没有区间 [k, (r-l+1)/2) 的数。

我们求 P 的时候是从根向叶子求的,所以我们可以同时维护根到某个节点 j 的所有数中,大于等于 k 的数中最小的一个。

然后我们对于每一个右端点,只要判断这个点到根的路径中大于等于 k 的数中最小的一个是否小于 (r - l + 1) / 2 就可以了。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<algorithm>
 7 using namespace std;
 8 const int INF=999999999;
 9 int ans=0,p[1500005],num[1500005],n,k;
10 char st[1500005];
11 int main()
12 {
13     gets(st+1);
14     scanf("%d",&k);
15     int n=strlen(st+1);
16     for (int i=1; i<=n; i++){
17         int j=0;
18         p[1]=0; num[0]=INF;
19         for (int t=2; t<=n-i+1; t++){
20             while (j>0 && st[i+j]!=st[t+i-1]) j=p[j];
21             if (st[i+j]==st[i+t-1]) j++;
22             p[t]=j;
23             if (j<k) num[j]=INF; else num[j]=min(j,num[p[j]]);
24             if ((num[j]<<1)<t) ans++;
25         }        
26     }
27     printf("%d\n",ans);
28     return 0;
29 }

miao~~~

posted @ 2018-07-31 17:24  wangyh1008  阅读(114)  评论(0编辑  收藏  举报