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