poj-2774-后缀数组

题目连接

poj

Height数组

在之前的博客中已经说了后缀数组的具体算法以及如何求出sa数组,但是求出sa之后我们需要学习如何运用sa数组。
第一个运用当然就是height数组了,他表示的意思是

height[i]表示第i小的后缀串与第i-1小的后缀串的最长公共前缀(如图)

20160205125636006.jpg
如果能够求出这个数组我们就能够在O(n)时间内求出最长公共子串了。
观察上面那个图,我们发现height有一个规律,它的数是3 2 1 0 3 2 1这样的数,然后我们思考,为什么会这样?我们看第一个3的地方是aabaaaab与aab的最长公共前缀,然后如果我们把它去掉第一个字母,哎就变成abaaaab和ab,是不是有些规律?我们就可以用这个规律来求出我们的height数组。
定义一个数组H[i]=height[rk[i]],也就是从i位置的后缀与在sa数组中排名比他小一名的字符串的最长公共前缀,如果我们从1-n的往后求H[i],就很惊讶的发现,竟然就是我们刚刚模拟的过程,所以这样我们就可以得到height数组了

	for(int i=1,j=0;i<=n;i++){
		if(j)j--;
		while(s[i+j]==s[sa[rk[i]-1]+j])j++;找出最长的,然后再递减的过程
		h[rk[i]]=j;
	}

思路

得到height数组后,如何求两个串的最长公共子串?把第二个串连接起来,然后遍历height的,当sa[i]与sa[i-1]记录的位置分别在两个串的时候,就记录答案,这就是最长公共子串

代码实现

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e6+10;
char str[N];
char s[N];
char s1[N];
int n,m;
int rk[N],tp[N],sa[N],tax[N];
int h[N];
void Rsort(){
		//第一关键字rk,第二关键字tp进行基数排序复杂度O(n) 
	for(int i=0;i<=m;i++)tax[i]=0;
	for(int i=1;i<=n;i++)tax[rk[tp[i]]]++;
	for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
	for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
		//基数排序完毕
}
bool comp(int *f,int x,int y,int w){return f[x]==f[y]&&f[x+w]==f[y+w];}
void getsa(){
	for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i;
	m=130;
	Rsort();
	for(int w=1,p=1;p<n;w+=w,m=p){
		//首先超过后缀不大于w的第二关键字肯定为0 
		p=0;
		for(int i=1;i<=w;i++)tp[++p]=n-w+i;
		//然后根据上一轮的排序求出剩下的第二关键字,从小到大,如果它的位置大于w,说明sa[i]-w这个位置的第二关键字大 
		for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
		Rsort();
		//把rk数组拿出来,需要求出sa数组,rk[sa[1]]=1是因为第一个位置的rk肯定是1 
		swap(tp,rk);
		rk[sa[1]]=p=1;
		//判断当前第一关键字与第二关键字的大小关系,使得没有相等的,若有相等则继续倍增即可 
		for(int i=2;i<=n;i++)
		rk[sa[i]]=comp(tp,sa[i],sa[i-1],w)?p:++p;
	}
	for(int i=1,j=0;i<=n;i++){
		if(j)j--;
		while(s[i+j]==s[sa[rk[i]-1]+j])j++;
		h[rk[i]]=j;
	}
}
int main(){
	scanf("%s %s",s1,str);
	int len1=strlen(s1);
	int len2=strlen(str);
	for(int i=1;i<=len1;i++){s[i]=s1[i-1];}
	for(int i=len1+1;i<=len2+len1;i++){s[i]=str[i-len1-1];}
	n=strlen(s+1);
	getsa();
	int ans = 0;
	for(int i=2;i<=n;i++){
		if((sa[i]>len1&&sa[i-1]<=len1)||(sa[i]<=len1&&sa[i-1]>len1))ans=max(h[i],ans);
	}
	printf("%d\n",ans);
}
发布了67 篇原创文章 · 获赞 4 · 访问量 4792

猜你喜欢

转载自blog.csdn.net/weixin_44203780/article/details/105295857
今日推荐