后缀数组的使用心得——POJ2774 最长连续公共子串
对于这道题,将两个字符串直接合并成为一个字符串,分别记录连个字符串结束的位置。
首先,应用黑暗圣典的模板,我们可以顺利得到height,rank,sa三个数组。
之后直接扫描1-n所有的位置,选出来一个,符合“两者都在不同子串的最大长度即可”。
此时我们会发现,sa数组记录了每个子串开头的位置,可以用于判断。
#include<iostream> #include<stdio.h> #include<string> #include<string.h> #include<vector> #include<algorithm> using namespace std; const long long MAXN=200233; const long long INF=1e8+233; char s[MAXN]; int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n; int rankk[MAXN],height[MAXN]; void getHeight() { int k=0; for(int i=0;i<n;++i)rankk[sa[i]]=i; for(int i=0;i<n;++i) { if(k)k--; int j=sa[rankk[i]-1]; while(s[i+k]==s[j+k])k++; height[rankk[i]]=k; } } void build_sa(int m) { int i,*x=t,*y=t2; for(int i=0;i<m;++i)c[i]=0; for(int i=0;i<n;++i)c[x[i]=s[i]]++; for(int i=1;i<m;++i)c[i]+=c[i-1]; for(int i=n-1;i>=0;--i)sa[--c[x[i]]]=i; for(int k=1;k<=n;k*=2) { int p=0; for(int i=n-k;i<n;++i)y[p++]=i; for(int i=0;i<n;++i)if(sa[i]>=k)y[p++]=sa[i]-k; for(int i=0;i<m;++i)c[i]=0; for(int i=0;i<n;++i)c[x[y[i]]]++; for(int i=0;i<m;++i)c[i]+=c[i-1]; for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(int i=1;i<n;++i) { x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; } if(p>=n)break; m=p; } } int main() { // cin.sync_with_stdio(false); scanf("%s",s); int len=strlen(s); scanf("%s",s+len); int len2=strlen(s); n=len2+1; build_sa(233); getHeight(); int maxx=-1; for(int i=1;i<n;++i) { // cout<<height[i]<<ends<<s+sa[i]<<endl; if((sa[i]>=len&&sa[i-1]<len)||(sa[i]<len&&sa[i-1]>=len))maxx=max(maxx,height[i]); } cout<<maxx<<"\n"; // cout<<s<<endl; // cout<<len<<ends<<len2<<endl; // return 0; }