Problem
给出一个长度为 n n n 的序列,求可重叠的 k k k 次最长重复子串
Solution
这是后缀数组一个很经典的应用,这里就不赘述了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 20005
using namespace std;
int n,m,k,x[N],y[N],d[N],S[N],height[N],Rank[N],sa[N],sum[N];
int discrete(){
sort(d+1,d+n+1);
int K=unique(d+1,d+n+1)-(d+1);
for(int i=1;i<=n;++i)
S[i]=lower_bound(d+1,d+K+1,S[i])-d;
return K;
}
void solve(){
int i,j;
for(i=0;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]=S[i]]++;
for(i=1;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[i]]--]=i;
for(j=1;j<=n;j<<=1){
int num=0;
for(i=n-j+1;i<=n;++i) y[++num]=i;
for(i=1;i<=n;++i)
if(sa[i]>j) y[++num]=sa[i]-j;
for(i=0;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]]++;
for(i=1;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[y[i]]]--]=y[i];
swap(x,y);
num=1,x[sa[1]]=1;
for(i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?num:++num;
if(n==num) break;
m=num;
}
}
void GetH(){
int i,k=0;
for(i=1;i<=n;++i) Rank[sa[i]]=i;
for(i=1;i<=n;++i){
if(Rank[i]==1) continue;
int j=sa[Rank[i]-1];
while(S[i+k]==S[j+k]) k++;
height[Rank[i]]=k;
if(k) k--;
}
}
bool check(int mid){
int num=1;
for(int i=2;i<=n;++i){
if(height[i]<mid) num=1;
else{
num++;
if(num>=k) return true;
}
}
return false;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d",&S[i]),d[i]=S[i];
m=discrete(),solve(),GetH();
int l=1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}