题意:求一个串的最长的出现不少于K次的子串长度。
题解:后缀自动机可以统计一个子串出现多少次。如果这个子串出现不少于K次。那么h数组必定有连续的k-1个不少于K。嗯。。。再来一个二分答案。。。就这么玩就行了
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define rank rk
const int MAX = 2e4+100;
const int MAXN = 1e6+100;
const int INF = 0x3f3f3f3f;
int cntA[MAXN],cntB[MAXN],A[MAX],B[MAX],rank[MAX],tsa[MAX],SA[MAX],h[MAX],ch[MAX];
int n,K;
void get_SA(){
memset(cntA,0,sizeof cntA);
for (int i=1;i<=n;i++) cntA[ch[i]]++;
for (int i=1;i<MAXN;i++) cntA[i]+=cntA[i-1];
for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;
rank[SA[1]]=1;
for (int i=2;i<=n;i++){
rank[SA[i]] = rank[SA[i-1]];
if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;
}
for (int step = 1;rank[SA[n]]<n;step<<=1){
for (int i=0;i<MAXN;i++) cntA[i]=cntB[i]=0;
for (int i=1;i<=n;i++){
cntA[A[i]=rank[i]]++;
cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
}
for (int i=1;i<MAXN;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
for (int i=n;i>=1;i--) tsa[cntB[B[i]]--]=i;
for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];
rank[SA[1]]=1;
for (int i=2;i<=n;i++){
rank[SA[i]]=rank[SA[i-1]];
if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;
}
}
}
void get_Height(){
for (int i=1,j=0;i<=n;i++){
if (j) j--;
while (ch[i+j]==ch[SA[rank[i]-1]+j])j++;
h[rank[i]]=j;
}
}
bool check(int k){
int mincnt=INF,maxcnt=-INF;
for (int i=2;i<=n;i++){
if (h[i]>=k){
mincnt = min(mincnt,i);
maxcnt = max(maxcnt,i);
if (maxcnt-mincnt+1>=K-1){
return true;
}
}else{
mincnt = INF;
maxcnt = -INF;
}
}
return false;
}
void print(){
for (int i=1;i<=n;i++){
printf("h[%d]=%d\n",i,h[i]);
}
}
int main(){
scanf("%d%d",&n,&K);
for (int i=1;i<=n;i++){
scanf("%d",ch+i);
ch[i]++;
}
get_SA();
get_Height();
// print();
int l=0;
int r=n;
while (r-l>1){
int mid = l+r>>1;
// cout<<l<<" "<<r<<" "<<mid<<endl;
if (check(mid)){
l=mid;
}else{
r=mid;
}
}
if (check(r)){
cout<<r<<endl;
}else{
cout<<l<<endl;
}
return 0;
}