现在的位置: 首页 > 综合 > 正文

poj 3261 Milk Patterns (字符串_后缀数组)

2013年10月15日 ⁄ 综合 ⁄ 共 1816字 ⁄ 字号 评论关闭

题目链接: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 4

1 2 2 3 3 2 2 3 3

8 4

1 2 3 2 2 2 3 1

8 4

1 2 3 2 3 2 3 1

8 2

1 2 3 2 3 2 3 1

8 1
1 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原创,但可以转载,因为我们是兄弟。

抱歉!评论已关闭.