http://poj.org/problem?id=3261
题意:N头牛,每头牛有一个编号,求可重叠的至少出现K次的最大子串。
思路: 后缀数组+二分 。 二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k 个相同的子串满足条件,否则不存在。这个做法的时间复杂度为O(nlogn)。
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define MAXN 20010
#define MAXN1 1000010
int num[MAXN] ;
int sa[MAXN] , rank[MAXN] ,height[MAXN] ;
int wa[MAXN1] , wb[MAXN1] ,wv[MAXN1],wd[MAXN1] ;
int N,M;
int cmp(int *r , int a , int b , int l){
return r[a] == r[b] && r[a+l] == r[b+l] ;
}
void DA(int *r,int n,int m){ //O(NlogN)
int i, j , p , *x=wa, *y=wb,*t ;
for( i = 0 ; i < m ; i++ ) wd[i] = 0 ;
for( i = 0 ; i < n ; i++ ) wd[x[i]=r[i]] ++ ;
for( i = 1 ; i < m ; i++ ) wd[i] += wd[i-1] ;
for( i = n-1 ;i >= 0 ; i-- ) sa[--wd[x[i]]] = i ;
for( j = 1 , p = 1 ; p < n; j *= 2 , m=p ){
for( p = 0 , 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 < n ; i++) wv[i] = x[y[i]] ;
for( i = 0 ; i < m ; i++) wd[i] = 0 ;
for( i = 0 ; i < n ; i++) wd[wv[i]] ++ ;
for( i = 1 ; i < m ; i++) wd[i] += wd[i-1] ;
for( i = n-1 ; i >= 0 ; i--) sa[ --wd[wv[i]]] = y[i] ;
for( t = x , x = y , y = t , p = 1 , x[ sa[0] ] = 0,i = 1;i < n ; i++){
x[sa[i]] = cmp( y ,sa[i-1] ,sa[i] , j ) ? p-1: p++;
}
}
}
void calHeight(int *r , int n){
int i , j , k = 0 ;
for( i = 1 ; i <= n ; i++) rank[sa[i]] = i ;
for( i = 0 ; i < n ; height[ rank[i++]]=k){
for( k ? k-- : 0 , j=sa[rank[i]-1]; r[i+k]==r[j+k] ; k++) ;
}
}
bool is_ok(int len){
int i = 1 ;
while(i <= N){
while(i <= N && height[i]<len) i++ ;
int c = 1 ;
while(i <= N && height[i]>=len){ //以height[]数组分组
i++ ;
c ++ ;
}
if(c >= M) return true ;
}
return false ;
}
int main(){
while(scanf("%d %d",&N,&M) == 2){
for(int i=0;i<N;i++){
scanf("%d",&num[i]);
num[i]++ ;
}
num[N] = 0 ;
DA(num,N+1,MAXN1);
calHeight(num, N) ;
int low, high , mid ;
low = 1 ; high = N ;
while(low < high){
mid = (low + high + 1) >> 1;
if(is_ok(mid))
low = mid ;
else
high = mid - 1;
}
printf("%d\n",low);
}
return 0 ;
}