[Usaco2006 Dec]Milk Patterns

Description

农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天
产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。
John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的
牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。
比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。

Input

* Line 1: 两个整数 N,K。
* Lines 2..N+1: 每行一个整数表示当天的质量值。

Output

* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度

Sample Input

8 2
1
2
3
2
3
2
3
1



Sample Output

4

思路:和上一题一样,求最长可重叠的重复子串,但多了一个限制,要至少出现k次。二分,按height分组后统计是否有一个组的后缀个数大于k,如果有,那么就表明这一组的公共前缀在原字符串里出现了k次以上,那么就符合要求,return true;
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxl=40010;
int sa[maxl],tsa[maxl],rank[maxl],trank[maxl],sum[1000010],n,s[maxl],k,ans,h[maxl];

void sorts(int j){
	memset(sum,0,sizeof(sum));
	for (int i=1;i<=n;i++) sum[rank[i+j]]++;
	for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
	for (int i=n;i;i--) tsa[sum[rank[i+j]]--]=i;
	
	memset(sum,0,sizeof(sum));
	for (int i=1;i<=n;i++) sum[rank[tsa[i]]]++;
	for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
	for (int i=n;i;i--) sa[sum[rank[tsa[i]]]--]=tsa[i]; 
}

void getsa(){
	for (int i=1;i<=n;i++) trank[i]=s[i];
	for (int i=1;i<=n;i++) sum[trank[i]]++;//for (;;);
	for (int i=1;i<=1000000;i++) sum[i]+=sum[i-1];
	for (int i=n;i;i--) sa[sum[trank[i]]--]=i;
	rank[sa[1]]=1;
	for (int i=2,p=1;i<=n;i++){
		if (trank[sa[i]]!=trank[sa[i-1]]) p++;
		rank[sa[i]]=p;
	}
	for (int j=1;j<=n;j*=2){
		sorts(j);
		trank[sa[1]]=1;
		for (int i=2,p=1;i<=n;i++){
			if (rank[sa[i]]!=rank[sa[i-1]]||rank[sa[i]+j]!=rank[sa[i-1]+j]) p++;
			trank[sa[i]]=p;
		}
		memcpy(rank,trank,sizeof(rank));
	}
}

void geth(){
	for (int i=1,j=0;i<=n;i++){
		if (rank[i]==1) continue;
		while (s[i+j]==s[sa[rank[i]-1]+j]) j++;
		h[rank[i]]=j;
		if (j) j--; 
	}
}

bool check(int lim){
	int len=1,maxs=0,mins=100000000;
	for (int i=2;i<=n;i++){
		if (h[i]<lim){maxs=0,mins=100000000,len=0;} 
		maxs=max(maxs,sa[i]);
		mins=min(mins,sa[i]);
		len++;
		if (len>=k) return true;
	}
	return false;
}

int main(){
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++)
		scanf("%d",&s[i]);
	getsa();
	geth();
	int l=1,r=20000,mid=(l+r)>>1;
	while (l<=r){
		if (check(mid)){
			l=mid+1;
			ans=mid;
		}
		else r=mid-1;
		mid=(l+r)>>1;
	}
	printf("%d\n",ans);
	//for (;;);
	return 0;
}


posted @ 2014-12-03 17:05  orzpps  阅读(122)  评论(0编辑  收藏  举报