POJ2774 - Long Long Message
Description
求两个字符串\(s_1,s_2(|s_1|,|s_2|\leq10^5)\)的最长公共子串。
Solution
复习一波后缀数组。
将两个串连接成s1+'#'+s2
,求后缀数组。若\(sa[i]\)与\(sa[i-1]\)不在同一串中,用\(h[i]\)更新答案。
时间复杂度\(O(|s|log|s|)\)。
Code
//Long Long Message
#include <cstdio>
#include <cstring>
int max(int x,int y) {return x>y?x:y;}
int const N=2e5+10;
int n; char s[N];
int sa[N],rank[N<<1],h[N];
int cnt[N],tmp[N],rank1[N];
int main()
{
scanf("%s",s+1); n=strlen(s+1);
int L1=n; s[++n]='#';
scanf("%s",s+n+1); n=strlen(s+1);
for(int i=1;i<=n;i++) cnt[s[i]]=1;
for(int i=1;i<=256;i++) cnt[i]+=cnt[i-1];
for(int i=1;i<=n;i++) rank[i]=cnt[s[i]];
for(int L=1;L<=n;L<<=1)
{
memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;i++) cnt[rank[i+L]]++;
for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--) tmp[cnt[rank[i+L]]--]=i;
memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;i++) cnt[rank[tmp[i]]]++;
for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--) sa[cnt[rank[tmp[i]]]--]=tmp[i];
int k=0;
for(int i=1;i<=n;i++)
{
if(rank[sa[i]]!=rank[sa[i-1]]||rank[sa[i]+L]!=rank[sa[i-1]+L]) k++;
rank1[sa[i]]=k;
}
memcpy(rank,rank1,sizeof rank1);
if(k>=n) break;
}
for(int i=1,k=0;i<=n;i++)
{
if(rank[i]==1) {h[1]=k=0; continue;}
if(k) k--;
while(s[i+k]==s[sa[rank[i]-1]+k]) k++;
h[rank[i]]=k;
}
int ans=0;
for(int i=1;i<=n;i++) if((sa[i]>L1)^(sa[i-1]>L1)) ans=max(ans,h[i]);
printf("%d\n",ans);
return 0;
}