POJ 2774 Long Long Message

原创 2012年03月23日 15:15:37

http://poj.org/problem?id=2774

题意:给定两个字符串 A 和 B ,求最长公共子串。


思路:后缀数组。(摘自罗穗骞的国家集训队论文)字符串的任何一个子串都是这个字符串的某个后缀的前缀。求 A 和 B 的最长公共子串等价于求 A 的后缀和 B 的后缀的最长公共前缀的最大值。如果枚举A和 B 的所有的后缀,那么这样做显然效率低下。由于要计算 A 的后缀和 B 的后缀的最长公共前缀,所以先将第二个字符串写在第一个字符串后面,中间用一个没有出现过的字符隔开,再求这个新的字符串的后缀数组。观察一下,看看能不能从这个新的字符串的后缀数组中找到一些规律。以 A=“ aaaba ”,B=“ abaa ”为例,如图 8 所示。

poj <wbr>2774 <wbr>: <wbr>Long <wbr>Long <wbr>Message <wbr>(后缀数组)
    
    那么是不是所有的 height 值中的最大值就是答案呢?不一定!有可能这两个后缀是在同一个字符串中的,所以实际上只有当suffix(sa[i-1])和suffix(sa[i]) 不是同一个字符串中的两个后缀时,height[i]才是满足条件的。而这其中的最大值就是答案。记字符串 A 和字符串 B 的长度分别为|A|和|B|。求新的字符串的后缀数组和 height 数组的时间是 O(|A|+|B|) ,然后求排名相邻 但原来不在同一个字符串中的两个后缀的height值的最大值,时间也是O(|A|+|B|),所以整个做法的时间复杂度为 O(|A|+|B|) 。时间复杂度已经取到下限,由此看出,这是一个非常优秀的算法。

代码:
#include<stdio.h>
#include<string.h>
#define MAXN 200010
#define MAX(a,b) (a) > (b) ? (a): (b) 
int num[MAXN] ;
char str[MAXN] ;
int len1 , len2 ,N,M;
int sa[MAXN] , rank[MAXN] ,height[MAXN] ;
int wa[MAXN] , wb[MAXN] ,wv[MAXN],wd[MAXN] ; 

int cmp(int *r , int a , int b , int l){
	return r[a] == r[b] && r[a+l] == r[b+l] ;	
}
void DA(int *r,int n,int m){		//O(NlogN)
	int i, j , p , *x=wa, *y=wb,*t ;
	for( i = 0 ; i < m ; i++ ) 	wd[i] = 0 ;
	for( i = 0 ; i < n ; i++ ) 		wd[x[i]=r[i]] ++ ;
	for( i = 1 ; i < m ; i++ )	 	wd[i] += wd[i-1] ;
	for( i = n-1 ;i >= 0 ; i-- ) 	sa[--wd[x[i]]] = i ;
	for( j = 1 , p = 1 ; p < n; j *= 2 , m=p ){
		for( p = 0 , 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++)	wd[i] = 0 ;
		for( i = 0 ; i < n ; i++)	wd[wv[i]] ++ ;
		for( i = 1 ; i < m ; i++)	wd[i] += wd[i-1] ;
		for( i = n-1 ; i >= 0 ; i--)	sa[ --wd[wv[i]]] = y[i] ;
		for( t = x , x = y , y = t , p = 1 , x[ sa[0] ] = 0,i = 1;i < n ; i++){
			x[sa[i]] = cmp( y ,sa[i-1] ,sa[i] , j ) ? p-1: p++;	
		} 	
	}
} 

void calHeight(int *r , int n){
	int i , j , k = 0 ;
	for( i = 1 ; i <= n ; i++)	rank[sa[i]] = i ;
	for( i = 0 ; i < n ; height[ rank[i++]]=k){
		for( k ? k-- : 0 , j=sa[rank[i]-1]; r[i+k]==r[j+k] ; k++) ;	
	}	
}
int main(){
	int k ;
	while(scanf("%s",str) == 1){
		len1 = strlen(str);
		k = 0 ;
		for(int i=0;i<len1;i++){
			num[k++] = str[i] - 'a' + 2 ;
		}
		num[k++] = 0 ;		//想到于'#' 
		scanf("%s",str);
		len2 = strlen(str);
		for(int i=0;i<len2;i++)
			num[k++] = str[i] - 'a' + 2 ;
		N = len1 + len2  ;
		M = 30;
		DA(num,N+1,M);
		calHeight(num,N);
		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(ans , height[i]);	
			}	
		}
		printf("%d\n",ans);
	}	
	
	return 0 ;	
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

POJ 2774 Long Long Message

Description The little cat is majoring in physics in the capital of Byterland. A piece of sad news ...

POJ 2774 Long Long Message 题解&代码

第二道后缀数组…其实感觉比上一道简单【23333 后缀数组的主要用法之一就是height[]…这道题就是把两个字符串用分隔符连接起来,求新字符串的height[],然后找到有效height的最大值即...

【POJ】【P2774】【Long Long Message】【题解】【hash】

传送门:http://poj.org/problem?id=2774

POJ 2774 Long Long Message

赤裸裸的模板题,刚开始忘记判断是否在同一字符串中了 #include #include #include using namespace std; typedef long long ll; #d...

[POJ2774][后缀数组求LCP]Long Long Message

[Problem Description] The little cat is majoring in physics in the capital of Byterland. A piece of...

POJ 2774 Long Long Message (后缀数组)

题目类型 后缀数组 题目意思 问两个最长 1e5 的字符串的最长公共子串 解题方法 在第一个字符串后添加一个区别于输入的所有字符的字符(例如 '#') 后再...

POJ 2774 Long Long Message

后缀数组

POJ 2774 Long Long Message

后缀数组(Suffix Array)+高度数组(LCP Array)

POJ 2774 Long Long Message (后缀数组)

题目地址:POJ 2774 后缀数组第一发!后缀数组真是太神奇了。。(好像每学一种新算法我都会这么说。。 原理研究了好长时间,还有代码的实现,论文作者罗穗骞的代码太简洁。。好难看懂QAQ,看了好长...

poj 2774 Long Long Message

poj 2774 Long Long Message题意:求两个字符串的最长公共子串、才开始学后缀数组,拿这个题来练了下手。在求sa的过程中理解基数排序的过程,对LHQ牛的模板理解就会更深入。感觉da...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)