题意:求两个字符串的最长公共子串。
题解:
首先如果这两个长字符串存在某个最长的公共子串,那么该子串一定分别是这两个串的后缀的前缀.所以我们将两个串中间加一个符号‘$’然后连接起来形成一个新串(还要添尾0).然后我们求这个新串的height数组值,我们从sa[1]到新串长sa[n-1]依次扫描字典序相邻的两个后缀的LCP,如果这两个后缀分别属于之前不同的两个串,那么他们的LCP值就可能是他们最长连续公共子串的长度。否则的话就不是。求出满足条件的最大值。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int MAX=200000+10;
int rk[MAX],sa[MAX],ht[MAX];
int s[MAX],buc[MAX];
int *x=new int[MAX],*y=new int[MAX];
void getsa(int n,int m)
{
int i,k,p;
for(i=0;i<m;i++) buc[i]=0;
for(i=0;i<n;i++) buc[x[i]=s[i]]++;
for(i=1;i<m;i++) buc[i]+=buc[i-1];
for(i=n-1;i>=0;i--) sa[--buc[s[i]]]=i;
for(k=1,p=1;p<n;k<<=1,m=p)
{
p=0;
for(i=n-1;i>=n-k;i--) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;i++) buc[i]=0;
for(i=0;i<n;i++) buc[x[y[i]]]++;
for(i=1;i<m;i++) buc[i]+=buc[i-1];
for(i=n-1;i>=0;i--) sa[--buc[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
{
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) x[sa[i]]=p-1;
else x[sa[i]]=p++;
}
}
return ;
}
void getlcp(int n)
{
int i,j,k=0;
for(i=0;i<n;i++) rk[sa[i]]=i;
for(i=0;i<n;ht[rk[i++]]=k)
for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
return ;
}
int main()
{
string a,b;
cin>>a;
cin>>b;
int len1=a.length();
int len2=b.length();
for(int i=0;i<len1;i++)
s[i]=a[i]-'a'+2;
s[len1]=1;
for(int i=0;i<len2;i++)
s[len1+1+i]=b[i]-'a'+2;
s[len1+len2+1]=0;
int n=len1+len2+2;
getsa(n,30);
getlcp(n);
int ans=0;
for(int i=1;i<n;i++)
{
int sa1=sa[i-1],sa2=sa[i];
if(sa1>sa2) swap(sa1,sa2);
if(0<=sa1&&sa1<len1&&len1<sa2&&sa2<=len1+len2)
ans=max(ans,ht[i]);
}
printf("%d\n",ans);
return 0;
}