题目分析
给出一个序列,求重复K次的最长子串。很明显后缀数组构造出来,然后利用height数组的性质,通过二分枚举,找到有没有一个连续的区间[i,j]中所有的height值都大于等于mid。这样很容易就得出答案了。
#include <ctime>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 20005;
int s[maxn],sa[maxn],t[maxn],t2[maxn],c[1000010];
int Rank[maxn],height[maxn];
void build_sa(int n,int m){ //求sa数组
int i,*x = t, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1){
int p = 0;
for(i = n-k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; 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);
p = 1,x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k]?p-1:p++;
if(p >= n) break;
m = p;
}
}
void getHeight(int n){
int i,j,k = 0;
for(i = 0; i < n; i++) Rank[sa[i]] = i;
for(i = 0; i < n-1; i++){
if(k) k--;
j = sa[Rank[i]-1];
while(s[i+k] == s[j+k]) k++;
height[Rank[i]] = k;
}
}
int solve(int N,int K){
int left = 1, right = N, tot, i, ans = 1;
while(left <= right){
int mid = (left+right)/2;
for(tot = 1, i = 1; i <= N; i++){
if(height[i] >= mid) tot++;
else tot = 1;
if(tot >= K) break;
}
if(tot >= K){
left = mid+1;
ans = mid;
}
else right = mid-1;
}
return ans;
}
int main(){
int N,K;
while(scanf("%d%d", &N, &K) != EOF){
memset(s, 0, sizeof(s));
for(int i = 0; i < N; i++) scanf("%d", &s[i]);
build_sa(N+1, maxn);
getHeight(N+1);
int ans = solve(N, K);
printf("%d\n", ans);
}
return 0;
}