测试地址:Milk Patterns
题目大意:求数列中至少出现K次的重复子串的最长长度。
做法:和重复子串有关的题目,一下子就想到后缀数组。先对数列求一遍后缀数组,再求出height数组。我们发现答案是具有单调性的,所以我们二分答案M,将排序后的后缀序列按height数组分成几个连续区间,如果height[i]<M就把SA[i-1]与SA[i]划分在两个区间内。然后观察每个区间内的后缀个数,如果一个区间内后缀个数≥K,则说明存在长度为M的出现至少K次的重复子串,如果每个区间内都没有则说明不存在。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,a[20010],x[50010]={0},y[20010],s[20010],t[1000010];
int rank[20010],sa[20010],height[20010];
void DA()
{
int up=1000000,p=1;
for(int i=1;i<=n;i++) x[i]=a[i];
while(p<n)
{
for(int i=1;i<=n;i++) y[i]=x[i+p];
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++) t[y[i]]++;
for(int i=1;i<=up;i++) t[i]+=t[i-1];
for(int i=up;i>=1;i--) t[i]=t[i-1];
t[0]=0;
for(int i=1;i<=n;i++) s[++t[y[i]]]=i;
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++) t[x[s[i]]]++;
for(int i=1;i<=up;i++) t[i]+=t[i-1];
for(int i=up;i>=1;i--) t[i]=t[i-1];
t[0]=0;
for(int i=1;i<=n;i++) y[++t[x[s[i]]]]=s[i];
up=1;
s[y[1]]=1;
for(int i=2;i<=n;i++)
{
if (x[y[i]]!=x[y[i-1]]||x[y[i]+p]!=x[y[i-1]+p]) up++;
s[y[i]]=up;
}
for(int i=1;i<=n;i++) x[i]=s[i];
p<<=1;
}
for(int i=1;i<=n;i++)
{
rank[i]=x[i];
sa[rank[i]]=i;
}
}
void calc_height()
{
for(int i=1,j=0;i<=n;i++)
{
if (rank[i]==1) continue;
while(a[i+j]==a[sa[rank[i]-1]+j]) j++;
height[rank[i]]=j;
if (j>0) j--;
}
height[n+1]=0;
}
bool check(int m)
{
int start=1;
for(int i=1;i<=n;i++)
{
if (height[i+1]<m)
{
if (i-start+1>=k) return 1;
start=i+1;
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
DA();
calc_height();
int l=0,r=n,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if (check(mid))
{
ans=max(ans,mid);
l=mid+1;
}
else r=mid-1;
}
printf("%d",ans);
return 0;
}