poj 2774 后缀数组(模板题)

点击打开链接

模板

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int M =1e6+20;
int len,len1,len2;
char s[M];
int sa[M];//sa[i]排名i的后缀编号 

int c[M];//计数数组 c[i]=j表示关键字<=i的有j个  
int t1[M],t2[M]; 
void SA(char *s,int n,int m)	//n为strlen(s)+1; m为最大字符的ascii码值
{

	int *x=t1;//x[i]=j表示后缀i的排名 (每一轮的rank) 
	
	int *y=t2;//y[p]=i;后缀i的第二关键字排名为p 
	
	int i;
	for(i=0;i<m;i++)
	c[i]=0;
	
	
	for(i=0;i<n;i++)
	{
		c[x[i]=s[i]]++;//长度为1时 x[i]的排名就为该字符ascii值	
	}
	
	for(i=1;i<m;i++)
	c[i]+=c[i-1];
	
	
	for(i=n-1;i>=0;i--)//长度(1)的名次数组 
	sa[--c[x[i]]]=i;//aabaaaab 若x[i]值相同的sa靠前的小 
	
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		for(i=n-k;i<n;i++)
		y[p++]=i;//第二关键字为0的排前面 
		for(i=0;i<n;i++)
		{
			//倍增:x+k后缀的第一关键字排名等于x后缀的第二关键字排名
			 
			if(sa[i]>=k)//只有编号大于k的才能作为第二关键字 
			{
				y[p++]=sa[i]-k;
			}
		}
		//通过x[],y[]计算每个后缀的长为2*k前缀的sa数组
		for(i=0;i<m;i++)
		c[i]=0;
		for(i=0;i<n;i++)
		c[x[y[i]]]++;//第二关键字排名为i的后缀y[i]的第一关键字排名
		//此时c[p]的值表示第一关键字排名为p的有多少个 
		
		for(i=1;i<m;i++)
		c[i]+=c[i-1];// 此时c[i]:第一关键字的排名为i时,小于等于它的个数 
		for(i=n-1;i>=0;i--)
		sa[--c[x[y[i]]]]=y[i];//后缀y[i]在它的2*k长度中排名为? 
		//后缀y[i]的第一关键字排名为c[x[y[i]]] 第一关键字相同时 此时看第二关键字
		//y[i]中i越小 第二关键字排名就越小 后缀y[i]排名就越小 (逆推的原因)
	
		//接着更新x为长度2*k的名次数字 
		swap(x,y);//令y表示名次数组(rank)
		p=1;//此时p记录排名的不同的个数
		x[sa[0]]=0;
		for(i=1;i<n;i++)
		{	
			x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
			//两个关键字相同则在x数组的排名是一样的 
			//y[sa[i]]==y[sa[i-1]],即这两个后缀的长k的第一关键字相同的情况下  
            //他们必定还存在第二关键需要比较  
		}
		if(p>=n) break;//字典序比较法得出 当前排名都不同时,最终排名就为此排名 
		m=p; //
	} 
}
int rank[M],height[M];//设 height[i]=LCP(Suffix(sa[i]),Suffix(sa[i-1]))后缀排名为i和i-1的LCP 
//则 h[i]=height[rank[i]]= 后缀i和它前一个后缀的 LCP
//h[i]>=h[i-1]-1
//h[i-1]为后缀i-1和它排在它前一个的后缀k的LCP
//同时去掉相同的首字符之后 后缀k+1和和后缀i的LCP为h[i]=h[i-1]-1 
//因为后缀k+1和后缀i名次不一定相邻 所以若存在后缀t为后缀i相邻 则h[i]>=h[i]-1; 
void Height()
{
	int i,j,k=0;
	for(i=0;i<len;i++)
	rank[sa[i]]=i;
	for(i=0;i<len;i++)
	{
		if(k)
		k--;//h[i]>=h[i-1]-1;先减一
		int j=sa[rank[i]-1];//前一个后缀 
		while(s[i+k]==s[j+k])
		k++;
		
		height[rank[i]]=k;
			
	}	
} 
void solve()
{
	int ans=0;
	for(int i=2;i<len;i++)
	{
		if(height[i]>ans)
		{
			//后缀sa[i]和sa[i-1]不能在同一个串中 
			if(sa[i-1]>=0&&sa[i-1]<len1&&sa[i]>len1&&sa[i]<len)
			{
				ans=height[i];
			}
			if(sa[i]>=0&&sa[i]<len1&&sa[i-1]>len1&&sa[i-1]<len)
			{
				ans=height[i];
			}
		}
	}
	printf("%d\n",ans);
}
int main()
{
	scanf("%s",s);
	len1=strlen(s);
	s[len1]=1;//分隔符 
	scanf("%s",s+len1+1);//拼接两个粗串	
	len2=strlen(s)-len1-1;
	s[len1+len2+1]=0;//末尾 
	len=len1+len2+2;
	SA(s,len,128);

	Height();
	
	solve();//求两个串的最长公共字串 
	 //首先如果这两个长字符串存在某个最长的公共子串,那么该子串一定分别是这两个串的后缀的前缀.
	 //将两个串拼接起来求最大的LCP&&两个后缀不属于同一个串 
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值