玲珑杯 第4次 String cut(暴力字符串)
题意:给你长度为n(<=100000)的字符串,问你任意删除一个字符后得到循环节最多的数量是多少
题解:最简单的想法就是枚举删除的字符,再kmp求循环节,但是时间复杂度为O(n*n)会超时
因此我们需要这样,需要删除的字符我们不能枚举,只能通过一些特别的方法来寻找或者判断
我们根据kmp求循环节的想法来枚举循环节长度(相当于是答案),因为循环节长度是(n-1)的因子少于log2(n)个
所以我们枚举了循环节的长度后可以使用O(n)来判断长度为m的循环节是否满足条件
此时我们可以这样想:先假设我们需要删除的字符在第一个循环节内,此时我们先找出[0,m]的字符,然后[m+1,2*m]与[0,n]
进行枚举匹配,当不同时就将[0,n]这一个向后跳一个在比较(当然只能跳一次),否则当需要删除的字符在第一个循环节后面,
这样我们[0,m-1]就一定是循环节,这样再与[0,n]枚举匹配判断就好了
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<string> #include<cstdio> #include<cstring> #include<iomanip> #include<stdlib.h> #include<iostream> #include<algorithm> using namespace std; #define eps 1E-8 /*注意可能会有输出-0.000*/ #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型 #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化 #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0 #define mul(a,b) (a<<b) #define dir(a,b) (a>>b) typedef long long ll; typedef unsigned long long ull; const int Inf=1<<28; const ll INF=1ll<<60; const double Pi=acos(-1.0); const int Mod=1e9+7; const int Max=1e5-7; char str[Max]; string tem; int Jud(int n,int dig,int num)//n长度 dig循环节长度 num循环节个数 { tem.clear(); for(int i=0; i<dig; ++i)//假设[0,m-1]为循环节 tem+=str[i]; int flag=0; for(int i=dig; i<n; ++i) { if(tem[(i+flag)%dig]!=str[i])//跳 { if(flag==-1) { flag=-2; break; } else { flag=-1; } } } if(flag!=-2) return 1; tem.clear(); for(int i=dig+1; i<dig+dig+1; ++i)//[m+1,2*m]为循环节 tem+=str[i]; flag=0; for(int i=0; i<n; ++i) { if(tem[(i+flag)%dig]!=str[i]) { if(flag==-1) { flag=-2; break; } else { flag=-1; } } } if(flag!=-2) return 1; return 0; } int main() { int t,n; scanf("%d",&t); while(t--) { scanf("%d",&n); scanf("%s",str); for(int i=1; i<=n-1; ++i)//枚举循环节长度 { if(i==n-1||(n-1)%i==0&&Jud(n,i,(n-1)/i))//判断 { printf("%d\n",(n-1)/i); break; } } } return 0; }