题目链接:http://poj.org/problem?id=3261
题目大意:给定一个序列,从中选择重复次数大于k次的的最长子段,可重叠
解题思路:本题可用后缀数组解决。先用倍增算法算出sa数组、rank数组再算出height数组,最后二分查找0-n的长度,找一个最长的长度符合重复数量大于k。做完这题能想到height数组中保存的最长公共前缀是像山一样的波浪形,有相同前缀的会扎堆。从某个位置开始,后面连续的height值都比它大的话,后面的值就和它有相同的公共前缀。那说到这里,本题的解法就自然而然出来,算到某个位置时它的height值如果大于mid,那向后查找连续的x位都比mid大,如果x大等于k,那就符合情况。
测试数据:
5 5
0 0 0 0 0
9 41 2 2 3 3 2 2 3 3
8 41 2 3 2 2 2 3 1
8 41 2 3 2 3 2 3 1
8 21 2 3 2 3 2 3 1
8 11 2 3 2 3 2 3 1
Output:
1
1
1
0
4
8
代码:
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 1000010
int n,renum,arr[MAX],brr[MAX];
int wn[MAX],wv[MAX],wa[MAX],wb[MAX];
int sa[MAX],rank[MAX],h[MAX];
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) {
int i,j,k,p;
int *x = wa,*y = wb,*t;
for (i = 0; i < m; ++i) wn[i] = 0;
for (i = 0; i < n; ++i) wn[x[i]=r[i]]++;
for (i = 1; i < m; ++i) wn[i] += wn[i-1];
for (i = n - 1; i >= 0; --i) sa[--wn[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) wn[i] = 0;
for (i = 0; i < n; ++i) wn[wv[i]]++;
for (i = 1; i < m; ++i) wn[i] += wn[i-1];
for (i = n - 1; i >= 0; --i) sa[--wn[wv[i]]] = y[i];
t = x,x = y,y = t,p = 1;
for (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; h[rank[i++]] = k)
for (k ? k-- : 0,j = sa[rank[i]-1];r[i+k] == r[j+k]; k++);
}
int Solve() {
int i,j,k,tot,maxx = 0;
int low,high,mid;
low = 1,high = n;
while (low <= high) {
mid = low + (high - low) / 2;
for (tot = 1,i = 1; i <= n; ++i) { //i是排名
if (h[i] >= mid) tot++;
else tot = 1;
if (tot >= renum) break;
}
if (tot >= renum) low = mid + 1,maxx = mid;
else high = mid - 1;
}
return maxx;
}
int main()
{
int i,j,k,ans;
while (scanf("%d%d",&n,&renum) != EOF) {
memset(arr,0,sizeof(arr));
for (i = 0; i < n; ++i)
scanf("%d",&brr[i]),arr[i] = brr[i];
arr[n] = 0;
Da(arr,n+1,MAX); //算后缀数组的时候要把最后一个算在内,排名才是从1开始的。
CalHeight(arr,n);
ans = Solve();
printf("%d\n",ans);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。