poj1743(后缀数组)

本题需要对s数组做一下调整,然后求数组s中最长不重叠重复子串

#include<cstdio>
#include<cstring>
const int maxn=20005;
int s[maxn],rank[maxn],height[maxn],sa[maxn],wa[maxn],wb[maxn],wv[maxn],ws[maxn],n;
int mind[maxn][15],maxd[maxn][15];
void adjust_s(int *s,int &n)
{
	int i=0,minv=200;
	for(i=0;i<n-1;i++)
	{
		s[i]=s[i+1]-s[i];
		minv=minv>s[i]?s[i]:minv;
	}
	for(i=0;i<n-1;i++)
	{
		s[i]-=minv;
	}
	n--;
	/*for(i=0;i<n;i++)
		printf("%d ",s[i]);
	printf("\n");*/
}
int cmp(int *r,int a,int b,int l)
{
	if(r[a]!=r[b]) return 1;
	if(a+l>=n && b+l>=n) return 0;
	if(a+l<n && b+l<n && r[a+l]==r[b+l]) return 0;
	return 1;
}
void build_sa(int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for(i=0;i<m;i++) ws[i]=0;
	for(i=0;i<n;i++) ws[x[i]=s[i]]++;
	for(i=1;i<m;i++) ws[i]+=ws[i-1];
	for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p)
	{
		p=0;
		for(i=n-j;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) ws[i]=0;
		for(i=0;i<n;i++) ws[wv[i]]++;
		for(i=1;i<m;i++) ws[i]+=ws[i-1];
		for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		t=x;x=y;y=t;p=1;x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)==0?p-1:p++;
	}
	/*for(i=0;i<n;i++)
		printf("%d ",sa[i]);
	printf("\n");*/
}
void get_height()
{
	int i,j,k=0;
	for(i=0;i<n;i++) rank[sa[i]]=i;
	for(i=0;i<n;i++)
	{
		if(k) k--;
		j=rank[i]-1;
		if(j==-1)
		{
			height[0]=0;
			continue;
		}
		j=sa[j];
		while(s[i+k]==s[j+k]) k++;
		height[rank[i]]=k;
	}
	/*for(i=0;i<n;i++)
		printf("%d ",rank[i]);
	printf("\n");
	for(i=0;i<n;i++)
		printf("%d ",height[i]);
	printf("\n");*/
}
int max(int a,int b)
{
	return a>b?a:b;
}
int min(int a,int b)
{
	return a>b?b:a;
}
void RMQ_INIT(int *r,int n)
{
	int i,j;
	for(i=0;i<n;i++) mind[i][0]=maxd[i][0]=r[i];
	for(j=1;(1<<j)<=n;j++)
	{
		for(i=0;i+(1<<j)-1<n;i++)
		{
			mind[i][j]=min(mind[i][j-1], mind[i+(1<<(j-1))][j-1]);
			maxd[i][j]=max(maxd[i][j-1], maxd[i+(1<<(j-1))][j-1]);
		}
	}
}
int RMQ_QUERY(int l,int r,int flag)
{
	int k=0;
	while((1<<(k+1))<(r-l+1)) k++;
    if(flag)
		return max(maxd[l][k], maxd[r-(1<<k)+1][k]);
	else
		return min(mind[l][k], mind[r-(1<<k)+1][k]);
}
int judge(int k)
{
	int i,j,v1,v2,start=1,end=0;
	for(i=1;i<n;i++)
	{
		if(height[i]>=k) end++;
		if((height[i]<k || i==n-1) && start<=end)
		{
			v1=RMQ_QUERY(start-1,end,0);
			v2=RMQ_QUERY(start-1,end,1);
			if(v2-v1>k)
				return 1;
			start=i+1;end=i;
		}
		if((height[i]<k || i==n-1) && start>end)
		{
			start=i+1;end=i;
		}
	}
	return 0;
}
int main()
{
	int i,j,mid,l,r;
	while(scanf("%d",&n)!=EOF&&n!=0)
	{
		for(i=0;i<n;i++)
			scanf("%d",&s[i]);
		if(n<10)
		{
			printf("0\n");
			continue;
		}
		adjust_s(s,n);
		build_sa(200);
		get_height();
		RMQ_INIT(sa,n);
		l=0;r=n/2;
		while(l<=r)
		{
			mid=(l+r)/2;

			if(judge(mid))
				l=mid+1;
			else
				r=mid-1;
		}
		printf("%d\n",l>=5?l:0);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值