题目链接: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 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原创,但可以转载,因为我们是兄弟。