poj1743 后缀数组+二分

原题:http://poj.org/problem?id=1743

题解:求最长主旋律,主旋律的定义是 长度大于5,重复出现(只要差值一样就可以),不重叠。对输入做差,求主串的不重叠子串的最大长度。很容易想到后缀数组求子串,同时二分主旋律的长度,检查是否有不重叠的子串就可以。二分L,凭借L可以将h[i]>=L,分成几组,h[i]<L求是这几组的分界线,如图(出自论文)判断 sa[i](max)-sa[j](min)>x就可

注意几处细节:

1.sa[i](max)-sa[j](min)>x 而不是>=x 给出数据:

21 
10 1 1 3 4 5 6 7 8 10 10 1 1 3 4 5 6 7 8 10 10

答案应该是10,因为我们记得是差分数组所有应该是大于

2.二分是应该倒序查找,因为h的定义第i和第i+1个后缀的LCP,不然会少记最后一个的s

给出代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Z=88,N=2e4+10,M=220,INF=1e18;
struct  node{int x,p;}b[N];
int s[N];
int a[N],sa[N],t[M<<1],h[N],rank[N<<1],rank1[N<<1],tmp[N],cnt[N];
int n,num;
bool cmp(node a,node b){return a.x<b.x;}
bool check(int x){
	int L=sa[n],R=sa[n];//记录最后一个sa[n] 
	for(int i=n;i>=1;i--){
		if(h[i]<x){
			L=R=sa[i] ;
		}else{
			L=min(L, sa[i]);
			R=max(R, sa[i] );
			if(R-L>x) return 1;
		}
	}
	return 0;
}
int main(){
//	freopen("test.in","r",stdin);
	while(scanf("%d",&n)!=EOF){
		if(n==0) break;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) s[i]=a[i]-a[i-1]+100;
	
		for(int i=0;i<=(n<<1);i++) rank[i]=rank1[i]=0;
		for(int i=0;i<=M;i++) t[i]=0;
		for(int i=1;i<=n;i++) t[s[i]]++;
		for(int i=1;i<M;i++) t[i]+=t[i-1];
		for(int i=1;i<=n;i++) rank[i]=t[s[i]];
		for(int p=1,k=0;k!=n;p<<=1){
			memset(cnt,0,sizeof cnt);
			for(int i=1;i<=n;i++) cnt[rank[i+p]]++;
			for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
			for(int i=n;i>=1;i--) tmp[cnt[rank[i+p]]--]=i;
			
			memset(cnt,0,sizeof cnt);
			for(int i=1;i<=n;i++) cnt[rank[i]]++;
			for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
			for(int i=n;i>=1;i--) sa[cnt[rank[tmp[i]]]--]=tmp[i];
			memcpy(rank1,rank,sizeof(rank)>>1);
			
			k=1; rank[sa[1]]=1;
			for(int i=2;i<=n;i++){
				if(rank1[ sa[i] ]!=rank1[sa[i-1]] || rank1[ sa[i] ]==rank1[sa[i-1]] && rank1[sa[i-1]+p]!=rank1[sa[i]+p] )k++;
				rank[sa[i]]=k;
			}
		}
		for(int i=1,k=1;i<=n;i++){
			if(rank[i]==n) {h[rank[i]]=0;continue;}
			if(k) k--;
			while(s[i+k]==s[sa[rank[i]+1]+k]) k++;
			h[rank[i]]=k;
		}
		
		int l=0;int r=n;
		while(l<=r){
			int mid=(l+r)>>1;
			if(check(mid)) l=mid+1;
			else r=mid-1;
		}
		if(l-1>=4) printf("%d\n",l);
		else printf("0\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39689721/article/details/87119721
今日推荐