【BZOJ1717】产奶的模式(后缀数组)

【BZOJ1717】产奶的模式(后缀数组)

题面

权限题
hihocoder
洛谷

题解

\(hihocoder\)里面讲的非常好了


这题要求的就是最长可重叠重复K次子串

所谓相同的子串
我们可以理解为如果有两个后缀的前缀相同
那么就有一个相同的子串

如果两个后缀的前缀相同
那么他们在\(SA\)中的排名是接近的

再说清楚点
如果两个后缀的前缀相同
必然是在后缀排序中一段连续的后缀都拥有这个相同的前缀

因此,求出\(height\)数组之后
考虑如何计算答案:
直接搞显然搞不出来
因此二分一下答案

如何\(check\)是否存在长度为\(mid\)\(K\)重复子串呢?
既然是一段连续的区间
因此,就需要检查是否有超过\(K\)个连续的\(height\)\(>=mid\)

这个很容易证明:
如果有\(l..r\)\(height\)都超过了\(mid\)
那么,证明这一段区间中任意两个后缀的
\(LCP\)长度都至少为\(mid\)
所以,此时这个\(mid\)一定出现了超过\(K\)

所以,大力二分一下即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 1200000
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=-1,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return x*t;
}
int SA[MAX],Rank[MAX],x[MAX],y[MAX],t[MAX];
int height[MAX],a[MAX];
int n,K;
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
	int m=1000010;
	for(int i=1;i<=n;++i)t[x[i]=a[i]]++;
	for(int i=1;i<=m;++i)t[i]+=t[i-1];
	for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		for(int i=1;i<=n;++i)y[i]=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=0;i<=m;++i)t[i]=0;
		for(int i=1;i<=n;++i)t[x[y[i]]]++;
		for(int i=1;i<=m;++i)t[i]+=t[i-1];
		for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
		swap(x,y);
		x[SA[1]]=p=1;
		for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
		if(p>=n)break;
		m=p;
	}
	for(int i=1;i<=n;++i)Rank[SA[i]]=i;
	for(int i=1,j=0;i<=n;++i)
	{
		if(j)j--;
		while(a[i+j]==a[SA[Rank[i]-1]+j])j++;
		height[Rank[i]]=j;
	}
}
bool check(int h)
{
	int cnt=0;
	for(int i=2;i<=n;++i)
	{
		if(height[i]<h)cnt=0;else cnt++;
		if(cnt==K-1)return true;
	}
	return false;
}
int main()
{
	n=read();K=read();
	for(int i=1;i<=n;++i)a[i]=read();
	GetSA();
	int l=1,r=n,ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans);
	return 0;
}

posted @ 2018-01-23 17:11  小蒟蒻yyb  阅读(706)  评论(0编辑  收藏  举报