题目大意:一个数组A,输出最长出现次数不小于k的子串(可交叉重叠)长度。A[i]<=1000000,n<=20000
思路分析:后缀数组。。。。离散下数组(不离散也可),二分长度,判断出现次数,
代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<set>
using namespace std;
int A[20010],ha[20010];
int sa[20010],c[20010],ra[20010],x[20010],y[20010],h[20010];
void build(int n,int m){
int i,j,p;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++){
x[i]=A[i]+1;
c[x[i]]++;
}
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=0;i<n;i++) sa[--c[x[i]]]=i;
for(j=1;j<n;j*=2){
p=0;
for(i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
x[sa[0]]=0;
m=1;
for(i=1;i<n;i++){
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=m-1;
else x[sa[i]]=m++;
}
if(m>=n) break;
}
}
void height(int n){
int i,j,k=0;
for(i=0;i<=n;i++) ra[sa[i]]=i;
for(i=0;i<n;i++){
if(k) k--;
j=sa[ra[i]-1];
while(A[i+k]==A[j+k]) k++;
h[ra[i]]=k;
}
}
int check(int l,int n,int k){
int sum=1,ma=-1,i;
for(i=1;i<=n;i++){
if(h[i]>=l){
sum++;
if(ma<sum) ma=sum;
}
else sum=1;
}
return ma>=k;
}
set<int>s;
set<int>::iterator it;
int main(){
int i,n,k,m;
scanf("%d%d",&n,&k);
for(i=0;i<n;i++){
scanf("%d",&A[i]);
s.insert(A[i]);
}
m=0;
for(it=s.begin();it!=s.end();it++) ha[m++]=*it;
for(i=0;i<n;i++){
A[i]=lower_bound(ha,ha+m,A[i])-ha;
// cout<<A[i]<<endl;
}
A[n]=-1;
build(n+1,20005);
height(n);
int l=1,r=20005,mid;
while(l+1<r){
mid=(l+r)/2;
if(check(mid,n,k)) l=mid;
else r=mid;
}
if(check(l,n,k)) printf("%d\n",l);
else printf("0\n");
return 0;
}