3160 最长公共子串

http://codevs.cn/problem/3160/

 时间限制: 2 s
 空间限制: 128000 KB
 
题目描述 Description

给出两个由小写字母组成的字符串,求它们的最长公共子串的长度。

 
输入描述 Input Description

读入两个字符串

 
输出描述 Output Description

输出最长公共子串的长度

 
样例输入 Sample Input
yeshowmuchiloveyoumydearmotherreallyicannotbelieveit
yeaphowmuchiloveyoumydearmother
 
样例输出 Sample Output

27

 
数据范围及提示 Data Size & Hint

单个字符串的长度不超过100000

将两个字符串中间用未出现过的字符连接起来

后缀数组

将后缀按rank排序求出height数组

然后枚举i height数组,如果sa[i]和sa[i-1]分别位于连接字符的两侧,则ans=max(ans,height[i])

看后缀数组戳这里http://www.cnblogs.com/TheRoadToTheGold/p/6591534.html

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
int n,p,q=,k,a[*N],rk[][N*],sa[][N*],h[N*],v[N*],ans;
char s1[N*],s2[N];
int len1,len2;
void mul(int *sa,int *rk,int *SA,int *RK)
{
for(int i=;i<=n;i++) v[rk[sa[i]]]=i;
for(int i=n;i;i--) if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+;i<=n;i++) SA[v[rk[i]]--]=i;
for(int i=;i<=n;i++) RK[SA[i]]=RK[SA[i-]]+(rk[SA[i]]!=rk[SA[i-]]||rk[SA[i]+k]!=rk[SA[i-]+k]);
}
void presa()
{
for(int i=;i<=n;i++) v[a[i]]++;
for(int i=;i<=;i++) v[i]+=v[i-];
for(int i=;i<=n;i++) sa[p][v[a[i]]--]=i;
for(int i=;i<=n;i++) rk[p][sa[p][i]]=rk[p][sa[p][i-]]+(a[sa[p][i]]!=a[sa[p][i-]]);for(k=;k<n;k<<=,swap(p,q))
{
mul(sa[p],rk[p],sa[q],rk[q]);
if(rk[q][sa[q][n]]==n)
{
swap(p,q);
return;
}
}
}
void getheight()
{
int j;
for(int i=,g=;i<=n;i++)
{
/*if(rk[p][i]==1) 有没有无所谓 因为h[1]不参与计算
{
h[rk[p][i]]=0;
continue;
}*/
j=sa[p][rk[p][i]-];
while(a[i+g]==a[j+g]) g++;
h[rk[p][i]]=g; if(g) g--;
}
}
void solve()
{
int t1,t2;
for(int i=;i<=n;i++)
{
t1=sa[p][i];
t2=sa[p][i-];
if((t1>=len1)^(t2>=len1)) ans=max(ans,h[i]);
}
printf("%d",ans);
}
int main()
{
scanf("%s%s",s1+,s2);
len1=strlen(s1+);len2=strlen(s2);
n=len1;
s1[++n]='z'+;
for(int i=;i<len2;i++) s1[++n]=s2[i];
for(int i=;i<=n;i++) a[i]=s1[i]-'a'+;
presa();
getheight();
solve();
}

2个错误:

① 连接字符的选用,这个字符并不能随便选,它关系到presa()中第二重循环的范围

刚开始用的‘$’,但presa()中第二重循环仍到26,错了,

修改为选用ascll码在z后一个的字符

② solve()中,应该两个后缀分列连接字符两侧即可,一开始写的第一个在左侧,第二个在右侧

05-13 00:47