bzoj 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式(后缀数组)

1717: [Usaco2006 Dec]Milk Patterns 产奶的模式

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 903   Solved: 495
[ Submit][ Status][ Discuss]

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

HINT

Source

Gold

[ Submit][ Status][ Discuss]


题解:后缀数组解决可重叠的k次最长重复子串。

首先要利用后缀数组求出sa,rank,h (h[i]表示排名第I的后缀与排名i-1的后缀的最长公共前缀)

然后二分答案,判断是否存在连续至少k-1个位置的h值大于mid


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 20003
#define M 1000003
using namespace std;
int n,m,p,q,k;
int a[N],v[M],rank[2][N],sa[2][N],h[N],ht[N];
void solve(int sa[N],int rank[N],int sa1[N],int rank1[N])
{
	for (int i=1;i<=n;i++) v[rank[sa[i]]]=i;
	for (int i=n;i>=1;i--)
	 if (sa[i]>k)
	  sa1[v[rank[sa[i]-k]]--]=sa[i]-k;
	for (int i=n-k+1;i<=n;i++)
	 sa1[v[rank[i]]--]=i;
	for (int i=1;i<=n;i++)
	 rank1[sa1[i]]=rank1[sa1[i-1]]+(rank[sa1[i]]!=rank[sa1[i-1]]||rank[sa1[i]+k]!=rank[sa1[i-1]+k]);
}
void work()
{
	p=0; q=1;
	for (int i=1;i<=n;i++) v[a[i]]++;
	for (int i=1;i<=n;i++) v[i]+=v[i-1];
	for (int i=1;i<=n;i++)  sa[p][v[a[i]]--]=i;
	for (int i=1;i<=n;i++)
	 rank[p][sa[p][i]]=rank[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
	k=1;
	while (k<=n)
	{
		solve(sa[p],rank[p],sa[q],rank[q]);
		p^=1; q^=1; k<<=1;
	}
}
void calc()
{
	int k=0;
	for (int i=1;i<=n;i++)
	 if (rank[p][i]==1)  h[rank[p][i]]=0;
	 else
	 {
	 	int j=sa[p][rank[p][i]-1];
	 	while (a[i+k]==a[j+k]) k++;
	 	h[rank[p][i]]=k;
	 	if(k>0) k--;
	 }
}
int check(int x)
{
	int num=0;
	for (int i=1;i<=n;i++)
	 if (h[i]>=x)
	  {
	  	num++;
	  	if (num==m-1)  return 1;
	  }
	 else num=0;
	return 0;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	 scanf("%d",&a[i]),a[i]++;
	work();
	calc();
	int head=1; int tail=n;
	int ans=0;
	while (head<=tail)
	 {
	 	int mid=(head+tail)/2;
	 	if (check(mid))  ans=mid,head=mid+1;
	 	else tail=mid-1;
	 }
	printf("%d\n",ans);
}


你可能感兴趣的:(bzoj 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式(后缀数组))