Problem
给出两个由小写字母组成的字符串,求它们的最长公共子串的长度。
其中单个字符串的长度不超过 100000 100000 100000。
Solution
这也是后缀数组的一大经典应用。网上的资料非常清楚,我就不多说了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
char S[N];
int n,m,l,x[N],y[N],sa[N],Rank[N],height[N],sum[N];
void solve(){
int i,j;
for(i=1;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]=S[i]]++;
for(i=2;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[i]]--]=i;
for(j=1;j<=n;j<<=1){
int num=0;
for(i=n-j+1;i<=n;++i) y[++num]=i;
for(i=1;i<=n;++i)
if(sa[i]>j) y[++num]=sa[i]-j;
for(i=1;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]]++;
for(i=2;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[y[i]]]--]=y[i];
swap(x,y);
num=1,x[sa[1]]=1;
for(i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?num:++num;
if(n==num) break;
m=num;
}
}
void GetH(){
int i,k=0;
for(i=1;i<=n;++i) Rank[sa[i]]=i;
for(i=1;i<=n;++i){
if(Rank[i]==1) continue;
int j=sa[Rank[i]-1];
while(S[i+k]==S[j+k]) k++;
height[Rank[i]]=k;
if(k) k--;
}
}
int main(){
int i,ans=0;
scanf("%s",S+1),l=strlen(S+1);
S[l+1]='a'-1;
scanf("%s",S+l+2),n=strlen(S+1);
m='z',solve(),GetH();
for(i=2;i<=n;++i)
if((sa[i]<=l)^(sa[i-1]<=l))
ans=max(ans,height[i]);
printf("%d",ans);
return 0;
}