BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 二分答案 哈希/后缀数组

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

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

提供两种做法


看完题 想一想

权值范围十分优越,直接上哈希

长度是可二分的,直接O(nlogn)搞

判断用了一下map复杂的多了个log

最后复杂度O(nlog^2n)

(BJ巨懒,要不然完全可以写O(nlogn))

结果二分写错还WA两发


搞完之后看看别人怎么做的

后缀数组 简直男默女泪

这么一道 sa 题 竟然被我二分+哈希水过了 笑哭(当然这个做法一定是正确的)


二分 哈希

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<48||ch>57){if(ch=='-')f=-1;ch=getchar();}
	while(ch<=57&&ch>=48){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+48);}

const int N=20010,mod=int(1e9)+7,bas=131;

int n,m,a[N],mi[N],h[N];

map<int,int>mp;

inline int gethsh(int x,int len)
{return ((1ll*h[x+len-1]-1ll*h[x-1]*mi[len])%mod+mod)%mod;}

bool judge(int x)
{mp.clear();register int i;for(i=x;i<=n;++i)if(++mp[gethsh(i-x+1,x)]>=m)return 1;return 0;}

int main()
{
	n=read();m=read();
	register int i=1,l=0,r=n,mid;
	for(i=1;i<=n;++i)a[i]=read();mi[0]=1;
	for(i=1;i<=n;++i)
	{
		mi[i]=((1ll*mi[i-1]<<7)+(1ll*mi[i-1]<<1)+mi[i-1])%mod;
		h[i]=((1ll*h[i-1]<<7)+(1ll*h[i-1]<<1)+h[i-1]+a[i])%mod;
	}
	while(l<=r)
	{
		mid=l+r>>1;
		judge(mid)?l=mid+1:r=mid-1;
	}
	print(l-1);puts("");
	return 0;
} 
/*
8 2
1 2 3 2 3 2 3 1

4
*/


后缀数组

1A好开心

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<48||ch>57){if(ch=='-')f=-1;ch=getchar();}
	while(ch<=57&&ch>=48){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+48);}

const int N=20010,M=1000100;

int n,m,a[N];

int buc[M],sa1[N],sa2[N],rk1[N],rk2[N],ht[N];
int *sa=sa1,*rk=rk1,*tp=sa2,*tmp=rk2;

void getsa()
{
	register int i,j,k;
	register bool flag=0;
	for(i=1;i<=n;++i)buc[a[i]]++;
	for(i=1;i<M;++i)buc[i]+=buc[i-1];
	for(i=n;i;i--)sa[buc[a[i]]--]=i;
	for(i=1;i<=n;++i)rk[sa[i]]=rk[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]]);
	
	for(k=1;k<=n;k<<=1)
	{
		for(i=1;i<=n;++i)buc[rk[sa[i]]]=i;
		for(i=n;i;i--)if(sa[i]>k)tp[buc[rk[sa[i]-k]]--]=sa[i]-k;
		for(i=n-k+1;i<=n;++i)tp[buc[rk[i]--]]=i;
		for(i=1;i<=n;++i)
		{
			tmp[tp[i]]=tmp[tp[i-1]]+(rk[tp[i-1]]!=rk[tp[i]]||rk[tp[i-1]+k]!=rk[tp[i]+k]);
			if(tmp[tp[i]]==n)flag=1;
		}
		swap(rk,tmp);swap(sa,tp);if(flag)break;
	}
	
	k=0;
	for(i=1;i<n;++i)
	{
		j=rk[i]-1;
		while(a[sa[rk[i]]+k]==a[sa[j]+k])k++;
		ht[rk[i]]=k;if(k)k--;
	}
}

int q[N];
int main()
{
	n=read();m=read()-1;char ch[20];
	register int i,head=0,tail=0,ans=0;
	for(i=1;i<=n;++i)a[i]=read();
	getsa();
	for(i=2;i<=m;++i)
	{
		while(head<tail&&ht[i]<ht[q[tail-1]])tail--;
		q[tail++]=i;
	}
	for(i=m+1;i<=n;++i)
	{
		while(head<tail&&q[head]<=i-m)head++;
		while(head<tail&&ht[i]<ht[q[tail-1]])tail--;
		q[tail++]=i;
		ans=max(ans,ht[q[head]]);
	}
	print(ans);puts("");return 0;
}
/*
8 2
1 2 3 2 3 2 3 1

4
*/


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值