本题是翻开后缀数组专题的第一题。
题意:给你两个字符串A,B,求两个字符串的最长公共子串。
求两个字符串的最长公共子串也就是求两个字符串的两个后缀的最长公共前缀的最大值。
那么我们只要将B复制要A后面,中间加个分隔符。求出sa,rank,height三个数组。之后,我们只要求sa[i-1],sa[i]在不同字符串中且height[i]最大的height值。
代码:
#include<cstdio> #include<cstring> using namespace std; int l,n,ans; char s[200010]; int sa[200010],r[200010],rank[200010],height[200010],wa[200010],wb[200010],ws[200010],wv[200010]; 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 *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for (i=0;i<m;i++) ws[i]=0; for (i=0;i<n;i++) ws[x[i]=r[i]]++; for (i=1;i<m;i++) ws[i]+=ws[i-1]; for (i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0; for (i=0;i<n;i++) ws[wv[i]]++; for (i=1;i<m;i++) ws[i]+=ws[i-1]; for (i=n-1;i>=0;i--) sa[--ws[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 *sa,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() { scanf("%s",s); l=strlen(s); s[l]=1; scanf("%s",s+l+1); n=strlen(s); for (int i=0;i<n;i++) r[i]=s[i]; r[n]=0; da(r,sa,n+1,128); calheight(r,sa,n); for (int i=2;i<=n;i++) if (height[i]>ans) if ((l<sa[i-1]&&l>sa[i])||(l>sa[i-1]&&l<sa[i])) ans=height[i]; printf("%d\n",ans); }