SP687 Repeats

· · 题解

题意

给定一个长度为 n 字符串,求重复次数最多的连续重复子串。输出重复次数。

数据范围

### 思路 考虑一个重复次数大于 $1$ 的子串。设它在原串中的起始位置为 $i$,**重复子串**的长度为 $len$。那么显然就有 $\sum_{k=1}^{len-1} s[i+k]=s[i+len+k]$。换句话说,后缀 $i$ 和后缀 $i+len$ 一定满足 $lcp(i,i+len) \geq len$。其中 $lcp$ 表示最长公共前缀的长度。而出现次数就是 $lcp/len+1$。后面的 $1$ 是子串 $[i,i+len-1]$,这部分不在公共子串里,需要额外加上。 利用后缀数组+ST表,可以做到 $O(n \log n)$ 预处理,$O(1)$ 查询任意两个后缀的最长公共前缀。 可以枚举重复子串的长度 $len$。再枚举子串的位置,判断是否合法。这里需要注意,可以先枚举 $1,len,len \times 2+1$ 这些位置,假设当前的枚举到 $i$, 先往后求出后缀 $i$ 和后缀 $i+len$ 的最长公共子串,由于 $i$ 不一定是起始位置,还需要再往前枚举(因为每次都会往前枚举,所以每次往前枚举最多只需枚举到 $i-len$ 即可),不合法时就直接退出枚举。 一些细节见代码。 ### code: ```cpp #include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=2e5+10; char s[N]; int T,n,m,f[N][25],x[N],y[N],c[N],sa[N],rk[N],height[N]; void get_sa() { memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) c[x[i]=s[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i>=1;i--) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { int num=0; for(int i=n-k+1;i<=n;i++) y[++num]=i; for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k; for(int i=1;i<=m;i++) c[i]=0; for(int i=1;i<=n;i++) c[x[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0; swap(x,y);x[sa[num=1]]=1; for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num; if(num==n) break;m=num; } } void get_height() { memset(height,0,sizeof(height)); for(int i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,k=0;i<=n;i++) { if(rk[i]==1) continue; if(k) k--; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++; height[rk[i]]=k; } } void build_ST() { memset(f,0,sizeof(f)); for(int i=2;i<=n;i++) f[i][0]=height[i]; for(int j=1;j<=20;j++) for(int i=2;i+(1<<j-1)<=n;i++) f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]); } int lcp(int i,int j) { int k=log2(j-i+1); return min(f[i][k],f[j-(1<<k)+1][k]); } int main() { int T; cin>>T; while(T--) { cin>>n;for(int i=1;i<=n;i++) cin>>s[i]; m=122;get_sa();get_height();build_ST(); int ans=0,anslen=0,l=0,tot=0; for(int len=1;len<=n/2;len++) { for(int i=1;i+len<=n;i+=len) { if(s[i]!=s[i+len]) continue; int x=rk[i],y=rk[i+len]; if(x>y) swap(x,y); int now=lcp(x+1,y); if(now/len+1>ans) {ans=now/len+1;l=i;tot=ans*len;} int t=1; while(i-t>0&&t<=len&&s[i-t]==s[i-t+len]) { if((now+t)/len+1>ans) {ans=(now+t)/len+1;l=i-t;tot=ans*len;} t++; } } } cout<<ans<<endl; } return 0; } ```