撸串
直接考虑有几种情况
发现只能容错一次
那就好搞了
枚举循环节长度L
case 1:错误不在区间[1,L]中
那么循环节就是[1,L],直接hash判断是否后面是否相同,当遇到第一个不同时,就直接找出不同的删掉
case 2:错误在区间[1,L]中
那么循环节就一定是[L+1,L*2+1],和上面的一样判断
然后就没了。
主要就是枚举循环节长度L这个有点难想,一旦想到了那就简单了
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=200005;const int mod=1000000007; int T,n;char s[N];ll hash[N],H[N]; inline ll read() { char c=getchar();ll a=0,b=1; for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1; for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48; return a*b; } inline void hash_pre() { hash[0]=1; for(int i=1;i<=N-4;i++) { hash[i]=233*hash[i-1]%mod; } } inline int equal(int l,int r,int a,int b) { return (((H[r]-H[l-1]*hash[r-l+1]%mod+mod)%mod)-(H[b]-H[a-1]*hash[b-a+1]%mod+mod)%mod)==0; } inline int miss(int l,int r,int a,int b) { int L=0,R=b-a+1,ans; while(L<=R) { if(equal(l,l+((L+R)>>1)-1,a,a+((L+R)>>1)-1)) { ans=((L+R)>>1); L=((L+R)>>1)+1; } else R=((L+R)>>1)-1; } return ans+1; } int main() { freopen("string.in","r",stdin); freopen("string.out","w",stdout); T=read(); hash_pre(); int i,j; while(T--) { n=read(); scanf("%s",s+1); H[0]=0; if(n<2) { puts("0"); break; } for(i=1;i<=n;i++) { H[i]=((H[i-1]*233)%mod+s[i])%mod; } for(i=1;i<n;i++) { if(i*2+1<=n) { int az=miss(1,i,i+2,i*2+1); if(az==n+1||(az<=n&&equal(az+1,i+1,i+1+az,i+i+1))) { for(j=i*2+2;j<=n-i+1;j+=i) { if(equal(2+i,i+i+1,j,j+i-1)==false)break; } if(j+i-1>n&&equal(i+2,i+2+n-j,j,n))break; } } else { int az=miss(1,n-i-1,i+2,n); if(az==n-i)break; else { if(equal(az+1,n-i,i+az+1,n))break; } } for(j=i+1;j+i-1<=n;j+=i) { if(!equal(1,i,j,i+j-1))break; } if(j+i-1<=n) { int az=miss(1,i,j,i+j-1); if(j+i-1==n&&equal(az,i-1,j+az,n))break; else if(equal(az,i,j+az,j+i)) { for(j+=i+1;j+i-1<=n;j+=i) { if(!equal(1,i,j,i+j-1))break; } if(j>n||(j+i-1>n&&equal(1,n-j+1,j,n)))break; } } else { if(equal(1,n-j+1,j,n))break; else { int az=miss(1,n-j+1,j,n); if(equal(az,n-j,az+j,n))break; } } } cout<<i<<endl; } return 0; }