【POJ1743】Musical Theme(后缀数组)

【POJ1743】Musical Theme(后缀数组)

题面

洛谷,这题是弱化版的,\(O(n^2)dp\)能过
hihoCoder 有一点点区别
POJ 多组数据

题解

要求的是最长不可重叠重复子串
也就是找两个最长的相同子串
使得它们不相交

先求出\(SA,height\)
考虑一下如果两个子串相同
那么也就是两个后缀的前缀相同

还是一样吧。
二分答案,长度为\(K\)
那么,现在要找的就是连续长度不小于\(K\)\(height\)
如果一段连续的\(height\)都不小于\(K\)
证明这段区间的任意两个后缀的\(LCP\)长度都不小于\(K\)

因为要不相交
所以记录一下这段区间的\(SA\)最大值和最小值
这样就很容易的检查是否存在相交的情况
直接二分一下就好啦


当然了\(POJ\)和洛谷的题目没有这么直接
现在存在一个"转调"的问题
但是,不管怎么转
相邻的差是不会变的
所以相邻的两个求一下差再来做就行了

#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 120000
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],x[MAX],y[MAX],t[MAX];
int Rank[MAX],height[MAX];
int n,a[MAX];
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
	int m=1500;
	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=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 k)
{
	int mm,mi;
	for(int i=1;i<=n;++i)
	{
		if(height[i]<k)
			mm=mi=SA[i];
		else
		{
			mi=min(mi,SA[i]);
			mm=max(mm,SA[i]);
			if(mm-mi>k)return true;
		}
	}
	return false;
	
}
int main()
{
	while(233)
	{
		n=read();
		if(!n)break;
		memset(SA,0,sizeof(SA));
		memset(height,0,sizeof(height));
		memset(Rank,0,sizeof(Rank));
		memset(x,0,sizeof(x));memset(y,0,sizeof(y));
		memset(t,0,sizeof(t));memset(a,0,sizeof(a));
		for(int i=1;i<=n;++i)a[i]=read();
		for(int i=1;i<=n;++i)a[i]=a[i+1]-a[i]+100;
		GetSA();
		int l=1,r=n,ans=0;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(check(mid))l=mid+1,ans=mid;
			else r=mid-1;
		}
		printf("%d\n",ans>=4?ans+1:0);
	}
}

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