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 }
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 }
所以我放那么多时间在一个水题上有什么意义。