poj Musical Theme(后缀数组)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq872425710/article/details/82749046

前言:

jz大佬说初二不会后缀数组就退役吧,然后我看了看高二的自己竟然苟过了三年…哈哈哈哈哈哈

题目大意:

求一个最长重复不相交的字符串,长度必须大于5

思路:

因为原题比较长,也没有上面说的那么裸,所以我们先用每个数剪掉前面一个数,然后就转化成上面那样了,
然后用后缀数据求出来就好了。

具体后缀数组应用:

我们先分析一个判定性问题,如何判断两个字符串是相同且不重叠的。解决这个问题要用到里面的height数组。我们把排序后的后缀分成若干组,每个height值不小于一个下限。可以得到最长公共前缀长度不小于k的两个后缀一定在同一组。然后对于每个后缀数组只需要判断后缀sa的最大值和最小值是否不小于k,如果是,则说明存在两个后缀,其公共前缀长度不小于k且互不重叠,反之不存在。(这是一种对height每组讨论的方法,经常使用)。

程序:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<map>
#include<utility>
#include<vector>
#include<set>
#include<algorithm>
#define N 20010
using namespace std;
struct node{int now,next;}d[N];
int x[N],h[N],rank[N],height[N],val[N][2],c[N],sa[N],pos[N];
int n;

void prefix(){
	memset(h,0,sizeof(h));
	for (int i=1;i<=n;i++) {
		if (rank[i]==1) h[i]=0;
		 else {
		 	int now=0;
	 		if (i>1&&h[i-1]>1) now=h[i-1]-1;
	 		while (now+i<=n&&now+sa[rank[i]-1]<=n&&x[now+i]==x[now+sa[rank[i]-1]]) 
	 			now++;
	 		h[i]=now;
	 	}
	}
	for (int i=1;i<=n;i++) height[rank[i]]=h[i];
}

void value(int u,int v,int i){
	d[i].next=c[u];
	c[u]=i;
	d[i].now=v;
}

void Sort(int l,int r){
	for (int k=1;k>=0;k--){
		memset(c,0,sizeof(c));
		for (int i=r;i>=l;i--) value(val[pos[i]][k],pos[i],i);
		int t=0;
		for (int i=0;i<=20000;i++) 
		  for (int j=c[i];j;j=d[j].next) pos[++t]=d[j].now;
	}
	int t=0;
	for (int i=1;i<=n;i++){
		if (val[pos[i]][0]!=val[pos[i-1]][0]||val[pos[i]][1]!=val[pos[i-1]][1]) t++;
		rank[pos[i]]=t;
	}
}

void array(){
	int t=1;
	while (t/2<=n){
		for (int i=1;i<=n;i++){
			val[i][0]=rank[i];
			val[i][1]=(((i+t/2<=n)?rank[i+t/2]:0));
			pos[i]=i;
		}
		Sort(1,n);
		t*=2;
	}
	for (int i=1;i<=n;i++) sa[rank[i]]=i;
}

bool cheak(int len){
	int Min=n+1,Max=0;
	for (int i=1;i<=n;i++)
	 if (height[i]<len){
	 	if (Max-Min>=len) return 1;
	 	Min=Max=sa[i];
	 } else {
	 	Min=min(Min,sa[i]);
	 	Max=max(Max,sa[i]);
	 }
	 if (Max-Min>=len) return 1;
	 return 0;
}

int search(int l,int r){
	while (l<=r){
		int mid=(l+r)/2;
		if (cheak(mid)) l=mid+1;
				else r=mid-1;
	}
	return r;
}

void solve(){
	for (int i=1;i<=n-1;i++) rank[i]=x[i]=x[i+1]-x[i]+88;
	n--;
	array();
	prefix();
	int ans=search(0,n)+1;
	ans=((ans<5)?0:ans);
	printf("%d\n",ans);
}

int main(){
	while (scanf("%d",&n),n>0){
		for (int i=1;i<=n;i++) scanf("%d",&x[i]);
		solve();
	}
}

猜你喜欢

转载自blog.csdn.net/qq872425710/article/details/82749046