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

前置技能:后缀数组

height[i]:排名第i的后缀与排名第i-1的后缀的最长公共前缀,也就是sa[i]和sa[i-1]的最长公共前缀

h[i]:以第i个字符为起点的后缀与排名在它前1名的后缀的最长公共前缀,h[i] = height[rank[i]]

LCP(i, j):排名第i的后缀与排名第j的后缀的最长公共前缀

性质:

①LCP(i, j) = min(height[k], k∈[i+1, j] )

②对于i>1 && rank[i]>1 一定有h[i]>=h[i-1]-1


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

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 1342   Solved: 727
[ 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


对于这道题,求出height数组之后就是简单的二分了

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int q, n, a[20010], Rank[20010], Temp[20010], cot[1000050], Sa[20010], height[20010];
int Jud(int x)
{
	int i, now;
	now = 1;
	for(i=2;i<=n;i++)
	{
		if(height[i]>=x)
		{
			now++;
			if(now>=q)
				return 1;
		}
		else
			now = 1;
	}
	return 0;
}
int main(void)
{
	int i, m, p, k, l, r;
	scanf("%d%d", &n, &q);
	for(i=1;i<=n;i++)
	{
		scanf("%d", &a[i]);
		a[i] += 1;
	}
	m = 1000020;
	memset(cot, 0, sizeof(cot));
	for(i=1;i<=n;i++)
	{
		Rank[i] = a[i];
		cot[a[i]]++;
	}
	for(i=1;i<=m;i++)
		cot[i] += cot[i-1];
	for(i=n;i>=1;i--)
		Sa[cot[a[i]]--] = i;
	for(k=1;k<=n;k*=2)
	{
		p = 0;
		for(i=n-k+1;i<=n;i++)
			Temp[++p] = i;
		for(i=1;i<=n;i++)
		{
			if(Sa[i]>k)
				Temp[++p] = Sa[i]-k;
		}
		memset(cot, 0, sizeof(cot));
		for(i=1;i<=n;i++)
			cot[Rank[i]]++;
		for(i=1;i<=m;i++)
			cot[i] += cot[i-1];
		for(i=n;i>=1;i--)
			Sa[cot[Rank[Temp[i]]]--] = Temp[i];
		swap(Rank, Temp);
		p = 1;
		Rank[Sa[1]] = 1;
		for(i=2;i<=n;i++)
		{
			if(Temp[Sa[i-1]]==Temp[Sa[i]] && Temp[Sa[i-1]+k]==Temp[Sa[i]+k])
				Rank[Sa[i]] = p;
			else
				Rank[Sa[i]] = ++p;
		}
		m = p;
	}
	k = 0;
	for(i=1;i<=n;i++)
	{
		k = max(k-1, 0);	//利用了性质:对于i>1 && rank[i]>1 一定有h[i]>=h[i-1]-1
		m = Sa[Rank[i]-1];
		while(a[i+k]==a[m+k])
			k++;
		height[Rank[i]] = k;
	}
	l = 1, r = n;
	while(l<r)
	{
		m = (l+r+1)/2;
		if(Jud(m))
			l = m;
		else
			r = m-1;
	}
	printf("%d\n", r);
}
/*
8 2
1 2 3 2 3 2 3 1
*/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值