【bzoj1717】[Usaco2006 Dec]Milk Patterns 产奶的模式

探讨USACO竞赛中的产奶模式问题,利用后缀数组寻找最长重复序列,给出详细算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

[ Submit][ Status][ Discuss]





后缀数组例题


详见罗穗骞的论文


PS.单哈希都能过。。。???????


代码:

#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;

const int INF = 2147483647;
const int maxn = 100100;
const int maxm = 1000100;

int n,m,k,ans,a[maxn];
int sa[maxn],height[maxn],rank[maxn],t1[maxn],t2[maxn],c[maxm];

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void build()
{
	int *x = t1 , *y = t2;
	for (int i = 1; i <= m; i++) c[i] = 0;
	for (int i = 1; i <= n; i++) c[x[i] = a[i]]++;
	for (int i = 1; i <= m; i++) c[i] += c[i - 1];
	for (int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
	for (int k = 1; k <= n; k <<= 1)
	{
		int p = 0;
		for (int i = n - k + 1; i <= n; i++) y[++p] = i;
		for (int i = 1; i <= n; i++) if (sa[i] > k) y[++p] = sa[i] - k;
		
		for (int i = 1; i <= m; i++) c[i] = 0;
		for (int i = 1; i <= n; i++) c[x[y[i]]]++;
		for (int i = 1; i <= m; i++) c[i] += c[i - 1];
		for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i];
		
		swap(x,y);
		x[sa[1]] = p = 1;
		for (int i = 2; i <= n; i++)
			x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p;
		if (p >= n) break;
		m = p;
	}
	
	for (int i = 1; i <= n; i++) rank[sa[i]] = i;
	int k = 1;
	for (int i = 1; i <= n; i++)
	{
		if (k) k--;
		int j = sa[rank[i] - 1];
		while (a[i + k] == a[j + k]) k++;
		height[rank[i]] = k;
	}
}

inline bool judge(int x)
{
	int l = 1;
	for (int i = 1; i <= n - 1; i++)
	{
		if (height[i + 1] < x)
		{
			if (i - l + 1 >= k) return 1;
			l = i + 1;
		}
	}
	if (n - l + 1 >= k) return 1;
	return 0;
}

int main()
{
	n = getint(); m = 1000100; k = getint();
	for (int i = 1; i <= n; i++) a[i] = getint();
	build();
	int l = 1,r = n;
	while (r - l > 1)
	{
		int mid = l + r >> 1;
		if (judge(mid))	l = mid;
		else r = mid;
	}
	if (judge(r)) ans = r;
	else ans = l;
	printf("%d",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值