题目描述 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=1,k,a[2*N],rk[2][N*2],sa[2][N*2],h[N*2],v[N*2],ans; char s1[N*2],s2[N]; int len1,len2; void mul(int *sa,int *rk,int *SA,int *RK) { for(int i=1;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+1;i<=n;i++) SA[v[rk[i]]--]=i; for(int i=1;i<=n;i++) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]); } void presa() { for(int i=1;i<=n;i++) v[a[i]]++; for(int i=1;i<=27;i++) v[i]+=v[i-1]; for(int i=1;i<=n;i++) sa[p][v[a[i]]--]=i; for(int i=1;i<=n;i++) rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);for(k=1;k<n;k<<=1,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=1,g=0;i<=n;i++) { /*if(rk[p][i]==1) 有没有无所谓 因为h[1]不参与计算 { h[rk[p][i]]=0; continue; }*/ j=sa[p][rk[p][i]-1]; while(a[i+g]==a[j+g]) g++; h[rk[p][i]]=g; if(g) g--; } } void solve() { int t1,t2; for(int i=2;i<=n;i++) { t1=sa[p][i]; t2=sa[p][i-1]; if((t1>=len1)^(t2>=len1)) ans=max(ans,h[i]); } printf("%d",ans); } int main() { scanf("%s%s",s1+1,s2); len1=strlen(s1+1);len2=strlen(s2); n=len1; s1[++n]='z'+1; for(int i=0;i<len2;i++) s1[++n]=s2[i]; for(int i=1;i<=n;i++) a[i]=s1[i]-'a'+1; presa(); getheight(); solve(); }
2个错误:
① 连接字符的选用,这个字符并不能随便选,它关系到presa()中第二重循环的范围
刚开始用的‘$’,但presa()中第二重循环仍到26,错了,
修改为选用ascll码在z后一个的字符
② solve()中,应该两个后缀分列连接字符两侧即可,一开始写的第一个在左侧,第二个在右侧