【USACO06DEC】牛奶模式(后缀数组+单调队列)

 题目描述见原题。

分析

这几乎是一个模板题...

如果一个子串在字符串中多次出现,那么它一定是若干个排名紧密的后缀的前缀。什么意思?看样例:

1 2 3 2 3 2 3 1

排序后得到height数组:

height             字符串

        0                       1

        1                       1 2 3 2 3 2 3 1

        0                       2 3 1

        2                       2 3 2 3 1

        4                       2 3 2 3 2 3 1

        0                       3 1

        1                       3 2 3 1

        1                       3 2 3 2 3 1

(首先得知道height的含义)上下对比就会发现上述的规律。我们知道一段L~R的height,这段后缀的LCP取L-1~R的最小值,那么这里就等价于取所有K-1长度区间height最小值的最大值。一个单调队列即可。另外,这题先用离散化好一些。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=20005;
int a[MAXN],b[MAXN],q[MAXN],N,K,M;
int rank[MAXN],sec_SA[MAXN],Rsort_cnt[MAXN];//rank表第i个后缀的排名,sec_SA表第二关键字排名为i的子串起点,count_sort基数排序用来计数的数组 
int SA[MAXN],height[MAXN]; //SA表排名为i的后缀的起点,height表第i个后缀与第i-1个后缀的最长公共前缀 

char c;
void scan(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

void Rsort()
{
	for(int i=0;i<=M;i++) Rsort_cnt[i]=0; //初始化所有第二关键字的计数为0 
	for(int i=1;i<=N;i++) Rsort_cnt[rank[sec_SA[i]]]++; //第二关键字计数
	for(int i=1;i<=M;i++) Rsort_cnt[i]+=Rsort_cnt[i-1]; //计算前面有多少 
	for(int i=N;i>=1;i--) SA[Rsort_cnt[rank[sec_SA[i]]]--]=sec_SA[i];
}//第二关键字排在i位的子串起点为sec_SA[i],它在上一轮的比较中排rank[sec_SA[i]],在计数数组中,
//包括它本身前边有Rsort_cnt[rank[sec_SA[i]]]个后缀,那么这一轮排名第Rsort_cnt个后缀的起点就是sec_SA[i],
//因为第一关键字已经排好了,直接看第二关键字。这里的第二关键字指的是后缀的后二分之一,因为用的是倍增的方法 

int cmp(int *f,int x,int y,int len) {return f[x]==f[y]&&f[x+len]==f[y+len];}

void Suffix()
{
	for(int i=1;i<=N;i++) rank[i]=a[i],sec_SA[i]=i; //初始只有第一位的相比较 
	M=N; Rsort(); //可能最多有M种排名 
	
	for(int len=1,cnt=0,i;cnt<N;len+=len,M=cnt) //len是长度,cnt是不同的排名个数 
	{
		for(cnt=0,i=N-len+1;i<=N;i++) sec_SA[++cnt]=i; //这一部分倍增后就越界了 
		for(i=1;i<=N;i++) if(SA[i]>len) sec_SA[++cnt]=SA[i]-len; //见大神博客的图,这样减少cnt
		Rsort(); swap(rank,sec_SA); rank[SA[1]]=cnt=1; //暂时用sec_SA存一下旧的rank 
		for(i=2;i<=N;i++) rank[SA[i]]=cmp(sec_SA,SA[i],SA[i-1],len)?cnt:++cnt; //由SA得到rank 
	}
	int j,k=0; //LCP 
	for(int i=1;i<=N;height[rank[i++]]=k)
	for(k=k?k-1:k,j=SA[rank[i]-1];a[i+k]==a[j+k];k++);
}

void solve()
{
	int front=0,rear=0,ans=0;
	for(int i=1;i<=N;i++)
	{
		while(rear!=front&&height[i]<height[q[rear-1]]) rear--;
		q[rear++]=i;
		while(rear!=front&&i-q[front]+1>K-1) front++;
		if(i>=K-1) ans=max(ans,height[q[front]]);
	}
	cout<<ans;
}

int main()
{
	scan(N);scan(K);
	for(int i=1;i<=N;i++)
	{
		scan(a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+N);
	int tot=unique(b+1,b+1+N)-b;
	for(int i=1;i<=N;i++) a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
	Suffix(); solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81545861