3160 最长公共子串
题目描述 Description
给出两个由小写字母组成的字符串,求它们的最长公共子串的长度。
输入描述 Input Description
读入两个字符串
输出描述 Output Description
输出最长公共子串的长度
样例输入 Sample Input
yeshowmuchiloveyoumydearmotherreallyicannotbelieveit
yeaphowmuchiloveyoumydearmother
yeaphowmuchiloveyoumydearmother
样例输出 Sample Output
27
数据范围及提示 Data Size & Hint
单个字符串的长度不超过100000
最近迷上字符串来hhhhh
这个题的解法比较巧妙,我们把两个字符串中间随便加一个不会出现的字符然后连起来做后缀数组,求出height之后,
如果sa[i]和sa[i-1]不在同一字符串中,我们就可以用height[i]更新答案。
这样得到的答案显然是最优的,因为求lcp是在height上的rmq,而显然rank不相邻的两个位置答案不会比相邻的更优。
#include<bits/stdc++.h> #define ll long long #define maxn 200005 using namespace std; char s[maxn]; int sa[maxn],sax[maxn]; int rank[maxn<<1],rankx[maxn]; int cc[maxn],height[maxn]; int n,m,len,ans=0,sec[maxn]; inline int pos(int x){ return x<n; } inline void get_height(){ for(int i=0;i<len;i++) cc[s[i]]++; for(int i=1;i<=500;i++) cc[i]+=cc[i-1]; for(int i=0;i<len;i++) sa[cc[s[i]]--]=i; for(int i=1;i<=len;i++){ rank[sa[i]]=i; if(i>1&&s[sa[i]]==s[sa[i-1]]) rank[sa[i]]=rank[sa[i-1]]; } int k=1; while(k<len){ memset(cc,0,sizeof(cc)); for(int i=0;i<len;i++) cc[sec[i]=rank[i+k]]++; for(int i=len-1;i>=0;i--) cc[i]+=cc[i+1]; for(int i=0;i<len;i++) sax[cc[sec[i]]--]=i; memset(cc,0,sizeof(cc)); for(int i=0;i<len;i++) cc[rank[i]]++; for(int i=1;i<=len;i++) cc[i]+=cc[i-1]; for(int i=1;i<=len;i++) sa[cc[rank[sax[i]]]--]=sax[i]; for(int i=1;i<=len;i++){ rankx[sa[i]]=i; if(i>1&&rank[sa[i]]==rank[sa[i-1]]&&sec[sa[i]]==sec[sa[i-1]]) rankx[sa[i]]=rankx[sa[i-1]]; } for(int i=0;i<len;i++) rank[i]=rankx[i]; k<<=1; } int now=0,j,mx; for(int i=0;i<len;i++){ if(rank[i]==1){ now=height[rank[i]]=0; continue; } if(now) now--; j=sa[rank[i]-1],mx=max(j,i); while(mx+now<len&&s[i+now]==s[j+now]) now++; height[rank[i]]=now; } } int main(){ scanf("%s",s); n=strlen(s); s[n]=‘~‘; scanf("%s",s+n+1); len=strlen(s); get_height(); for(int i=2;i<=len;i++) if(pos(sa[i])^pos(sa[i-1])) ans=max(ans,height[i]); printf("%d\n",ans); return 0; }