BZOJ3620 似乎在梦中见过的样子[KMP]

吐槽

迷惑行为大赏。。。

想好的$O(n^2)$暴力没敢写,结果正解就是15000卡常过$O(n^2)$。。

还耗了我一节课想SA的$O(n\text{log}n)$做法没想出来。。。


这题其实和动物园差不多,所谓$A+B+A$形式其实就是对于$i,j$,$\exists x \in [i,j]$使得$s[i...x]=s[j-(x-i),j]$且$x< j-(x-i)-1$。

于是变形式子得到关系式,就正常维护两个匹配的指针$j,k$进行匹配即可,$2k < i+j-1$且$s[k]=s[i]$即可匹配。

接下来要做的就是卡常。。其实kmp常数已经蛮小的啦,所以我不会卡常也过了。但其实我是榜底

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define dbg(x,y,z) cerr << #x << " = " << x << "  " << #y << " = " << y << "  " << #z << " = " << z <<endl
 7 using namespace std;
 8 typedef long long ll;
 9 typedef double db;
10 typedef pair<int,int> pii;
11 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
12 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
16 template<typename T>inline T read(T&x){
17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
19 }
20 const int N=15000+7;
21 char s[N];
22 int nxt[N];
23 int n,lim,ans;
24 inline void solve(int l){
25     nxt[l]=l-1;//dbg(l,0,0);
26     for(register int i=l+1,j=l-1,k=l-1;i<=n;++i){//dbg(i,j,k);
27         while(j>=l&&s[j+1]^s[i])j=nxt[j];
28         while(k>=l&&(s[k+1]^s[i]||k+1>=(l+i)>>1))k=nxt[k];
29         nxt[i]=s[j+1]^s[i]?l-1:++j;
30         s[k+1]==s[i]&&k+1<(l+i)>>1&&++k;
31         if(k-l+1>=lim)++ans;
32     }
33 }
34 
35 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
36     scanf("%s",s+1),read(lim);n=strlen(s+1);
37     for(register int i=1;i<=n-lim-lim;++i)solve(i);
38     return printf("%d\n",ans),0;
39 }
厚颜无耻放上贼慢的code

Upd:知道自己为什么常数大了。其实那个$k$没有必要加进去。考虑每个$i$尝试找最短的满足要求的前缀并且长度要$\ge k$。由于$next$指针可以看做一棵树,也就是求根到节点的路径上第一个出现的$\ge k$长度的前缀是否到了当前串长的一半。

那么可以用$fa[i]$表示在自己到祖先的所有前缀中最小的、长度$\ge k$的前缀是哪个,检查一下合法性即可。这其实是一个fail树思想的应用

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define dbg(x) cerr << #x << " = " << x <<endl
 7 using namespace std;
 8 typedef long long ll;
 9 typedef double db;
10 typedef pair<int,int> pii;
11 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
12 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
16 template<typename T>inline T read(T&x){
17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
19 }
20 const int N=15000+7,INF=114514;
21 char s[N];
22 int nxt[N],fa[N];
23 int n,lim,ans;
24 inline void solve(int l){
25     nxt[l]=l-1,fa[l-1]=fa[l]=INF;
26     for(register int i=l+1,j=l-1;i<=n;++i){
27         while(j^(l-1)&&s[j+1]^s[i])j=nxt[j];
28         nxt[i]=s[j+1]^s[i]?j:++j;
29         fa[i]=j<l+lim-1?INF:_min(j,fa[j]);
30         if(fa[i]<<1<l+i-1)++ans;
31     }
32 }
33 
34 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
35     scanf("%s",s+1),read(lim);n=strlen(s+1);
36     for(register int i=1;i<=n-lim-lim;++i)solve(i);
37     return printf("%d\n",ans),0;
38 }
优化后

所以我放那么多时间在一个水题上有什么意义。

posted @ 2019-09-11 15:50  Ametsuji_akiya  阅读(130)  评论(0编辑  收藏  举报