bzoj2946 [Poi2000]公共串(SA,SAM)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2946 [Poi2000]公共串(SA,SAM)相关的知识,希望对你有一定的参考价值。
Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
Input
文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3
abcb
bca
acbc
abcb
bca
acbc
Sample Output
2
【思路】
多串求LCS。
主要是想找一下SAM的优越感 :) velui good
后缀数组划分height需要注意不少细节 <_<,然后不停debug
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int N = 2000*5+10; 7 8 char s[N],tmp[N]; 9 int sa[N],c[N],t[N],t2[N],rank[N],height[N]; 10 11 void build_sa(int m,int n) { 12 int i,k,*x=t,*y=t2; 13 for(i=0;i<m;i++) c[i]=0; 14 for(i=0;i<n;i++) c[x[i]=s[i]]++; 15 for(i=1;i<m;i++) c[i]+=c[i-1]; 16 for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; 17 for(k=1;k<=n;k<<=1) { 18 int p=0; 19 for(i=n-k;i<n;i++) y[p++]=i; 20 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 21 for(i=0;i<m;i++) c[i]=0; 22 for(i=0;i<n;i++) c[x[y[i]]]++; 23 for(i=1;i<m;i++) c[i]+=c[i-1]; 24 for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; 25 swap(x,y); 26 p=1; x[sa[0]]=0; 27 for(i=1;i<n;i++) 28 x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]? p-1:p++; 29 if(p>=n) break; 30 m=p; 31 } 32 } 33 void get_height(int n) { 34 int i,j,k=0; 35 for(int i=0;i<=n;i++) rank[sa[i]]=i; 36 for(int i=0;i<n;i++) { 37 if(k) k--; 38 j=sa[rank[i]-1]; 39 while(s[j+k]==s[i+k]) k++; 40 height[rank[i]]=k; 41 } 42 } 43 44 int flag[6],cr[N]; 45 bool can(int M,int n,int k) { 46 int kase=0,cnt=1; 47 flag[cr[sa[0]]]=kase; 48 for(int i=1;i<n;i++) 49 if(height[i]<M) 50 cnt=1,flag[cr[sa[i]]]=++kase; 51 else { 52 int r=cr[sa[i]]; 53 if(flag[r]!=kase) cnt++,flag[r]=kase; 54 if(cnt==k) return true; 55 } 56 return false; 57 } 58 59 int main() { 60 int n; scanf("%d",&n); 61 int block=n,sz=0; 62 for(int i=0;i<n;i++) { 63 scanf("%s",tmp); 64 for(int j=0;tmp[j];j++) 65 s[sz]=tmp[j],cr[sz++]=i; 66 s[sz]=--block,cr[sz++]=i; 67 } 68 69 build_sa(‘z‘+1,sz); 70 get_height(sz); 71 72 int L=0,R=sz; 73 while(L<R) { 74 int M=L+(R-L+1)/2; 75 if(can(M,sz,n)) L=M; 76 else R=M-1; 77 } 78 printf("%d",L); 79 return 0; 80 }
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int N = 2000*5+10; 7 8 char s[N]; 9 int last,sz,fa[N],ch[N][26],l[N],mn[N],mx[N]; 10 int c[N],b[N]; 11 12 void add(int x) { 13 int c=s[x]-‘a‘; 14 int p=last,np=++sz; last=np; 15 mn[np]=l[np]=x+1; 16 for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; 17 if(!p) fa[np]=1; 18 else { 19 int q=ch[p][c]; 20 if(l[p]+1==l[q]) fa[np]=q; 21 else { 22 int nq=++sz; mn[nq]=l[nq]=l[p]+1; 23 memcpy(ch[nq],ch[q],sizeof(ch[q])); 24 fa[nq]=fa[q]; 25 fa[np]=fa[q]=nq; 26 for(;q==ch[p][c];p=fa[p]) ch[p][c]=nq; 27 } 28 } 29 } 30 31 int main() { 32 int n; scanf("%d",&n); 33 last=++sz; 34 scanf("%s",s); 35 for(int i=0;s[i];i++) add(i); 36 for(int i=1;i<=sz;i++) c[l[i]]++; 37 for(int i=1;i<=strlen(s);i++) c[i]+=c[i-1]; 38 for(int i=1;i<=sz;i++) b[c[l[i]]--]=i; 39 int ans=0; 40 for(int i=1;i<n;i++) { 41 scanf("%s",s); 42 int p=1,len=0; 43 for(int i=0;s[i];i++) { 44 int c=s[i]-‘a‘; 45 if(ch[p][c]) { len++; p=ch[p][c]; } 46 else { 47 while(p&&!ch[p][c]) p=fa[p]; 48 if(!p) { len=0; p=1; } 49 else { len=l[p]+1; p=ch[p][c]; } 50 } 51 mx[p]=max(mx[p],len); 52 } 53 for(int i=sz;i;i--) { 54 int p=b[i]; 55 mn[p]=min(mn[p],mx[p]); 56 if(fa[p]) mx[fa[p]]=max(mx[fa[p]],mx[p]); 57 mx[p]=0; 58 } 59 } 60 for(int i=1;i<=sz;i++) 61 if(ans<mn[i]) ans=mn[i]; 62 printf("%d",ans); 63 return 0; 64 }
以上是关于bzoj2946 [Poi2000]公共串(SA,SAM)的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2946][Poi2000]公共串_后缀数组_二分
BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案