[Codechef SSTORY] A Story with Strings
Description
给定两个字符串,求它们的最长公共子串。如果解不唯一,输出最先在第二个字符串中出现的那个。
Solution
仍然考虑二分答案,然后每次检验连续的一块中是否有来自两个串的后缀出现。
最后额外处理一次,每次检验连续的一块中是否有来自两个串的后缀出现,在有的情况下利用所有来源于第二个串的后缀得到可能的最小位置即可。
竟然二分边界忘记加一,我TM真是个憨憨
#include <bits/stdc++.h>
using namespace std;
int n,l1,l2,m=256,sa[1000005],y[1000005],u[1000005],v[1000005],o[1000005],r[1000005],h[1000005],T;
char str[1000005];
long long ans;
int main()
{
scanf("%s",str+1);
l1=strlen(str+1);
str[l1+1]='$';
scanf("%s",str+l1+2);
l2=strlen(str+l1+2);
n=l1+l2+1;
for(int i=1; i<=n; i++) u[str[i]]++;
for(int i=1; i<=m; i++) u[i]+=u[i-1];
for(int i=n; i>=1; i--) sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
for(int l=1; r[sa[n]]<n; l<<=1)
{
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1; i<=n; i++) u[r[i]]++, v[r[i+l]]++;
for(int i=1; i<=n; i++) u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n; i>=1; i--) y[v[r[i+l]]--]=i;
for(int i=n; i>=1; i--) sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1; i<=n; h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1]; str[i+k]==str[j+k]; k++);
}
int L=1,R=min(l1,l2)+1;
while(L<R)
{
int mid=(L+R)/2, flag=0;
int i=1,j=1;
while(i<=n && j<=n)
{
j=i;
while(h[j+1]>=mid) ++j;
int fg1=0,fg2=0;
for(int k=i; k<=j; k++)
{
if(sa[k]<=l1) fg1=1;
if(sa[k]>l1+1) fg2=1;
}
if(fg1 && fg2) flag=1;
i=j+1;
}
if(flag) L=mid+1;
else R=mid;
}
if(L-1==0)
{
printf("0
");
return 0;
}
int mx = n+1;
int i=1,j=1;
while(i<=n && j<=n)
{
j=i;
while(h[j+1]>=L-1) ++j;
int fg1=0,fg2=0;
for(int k=i; k<=j; k++)
{
if(sa[k]<=l1) fg1=1;
if(sa[k]>l1+1) fg2=1;
}
if(fg1 && fg2)
{
for(int k=i; k<=j; k++)
if(sa[k]>l1+1) mx=min(mx,sa[k]);
}
i=j+1;
}
for(int k=0; k<L-1; k++) printf("%c",str[mx+k]);
printf("
%d
",L-1);
}