POJ1743 Musical Theme【后缀数组】

假装没事ソ 提交于 2020-01-25 08:48:27

题目描述:

长度为n(<=20000)的整数序列(数值范围1到88),求长度大于等于5且最长的两个不重叠子串,使得其中一个整体加上或减去一个数后与另一个完全相同。输出长度。

题目分析:

设原串为a[i]a[i],令b[i]=a[i+1]a[i]b[i]=a[i+1]-a[i]b[l]b[l]b[r]b[r]对应于原串的a[l]a[l]a[r+1]a[r+1]
偏移后相同 等价于 差分后的串完全相同。
注意差分后两个串需要间隔一个位置。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 20005
using namespace std;
int b[maxn],ary[4][maxn],h[maxn];
int *sa=ary[0],*rk=ary[1],*nsa=ary[2],*nrk=ary[3];
void build_sa(int n,int m,int *a){
	memset(b,0,(max(n,m)+1)<<2),a[0]=a[n+1]=rk[n+1]=0;//!!!
	for(int i=1;i<=n;i++) b[a[i]]++;
	for(int i=1;i<=m;i++) b[i]+=b[i-1];
	for(int i=1;i<=n;i++) sa[b[a[i]]--]=i;
	for(int i=1;i<=n;i++) rk[sa[i]]=rk[sa[i-1]]+(a[sa[i-1]]!=a[sa[i]]);
	for(int k=1;rk[sa[n]]<n;k<<=1){
		for(int i=1;i<=n;i++) b[rk[sa[i]]]=i;
		for(int i=n;i>=1;i--) if(sa[i]>k) nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
		for(int i=n-k+1;i<=n;i++) nsa[b[rk[i]]--]=i;
		for(int i=1;i<=n;i++) nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i-1]]!=rk[nsa[i]]||rk[nsa[i-1]+k]!=rk[nsa[i]+k]);
		swap(sa,nsa),swap(rk,nrk);
	}
	for(int i=1,k=0,j;i<=n;h[rk[i]]=k,i++)
		for(k&&(k--),j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
}
int n,m,a[maxn];
bool check(int len){
	int mx,mn;
	for(int i=1;i<=n;i++)
		if(h[i]<len) mx=mn=sa[i];
		else if((mx=max(mx,sa[i]))-(mn=min(mn,sa[i]))>len) return 1;
	return 0;
}
int main()
{
	while(scanf("%d",&n),n){
		for(int i=0;i<n;i++) scanf("%d",&a[i]);
		for(int i=n-1;i>0;i--) a[i]=a[i]-a[i-1]+88;
		build_sa(--n,175,a);
		int l=3,r=n>>1,mid;
		for(;l<r;) mid=(l+r+1)>>1,check(mid)?(l=mid):(r=mid-1);
		printf("%d\n",l>=4?l+1:0);
	}
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!