poj1226-出现或反转后出现在每个串的最长公共子串后缀数组
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj1226-出现或反转后出现在每个串的最长公共子串后缀数组相关的知识,希望对你有一定的参考价值。
题意:求n个串的最长公共子串,子串出现在一个串中可以是它的反转串出现。总长<=10^4.
题解:
对于每个串,把反转串也连进去。
二分长度,分组,判断每个组。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 7 const int N=2*21000; 8 int n,sl,cl,c[N],rk[N],sa[N],Rs[N],wr[N],y[N],h[N],st[N],ed[N],fst[N],fed[N]; 9 char s[N]; 10 bool vis[N]; 11 12 void get_sa(int m) 13 { 14 for(int i=1;i<=cl;i++) rk[i]=c[i]; 15 for(int i=1;i<=m;i++) Rs[i]=0; 16 for(int i=1;i<=cl;i++) Rs[rk[i]]++; 17 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 18 for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i; 19 20 int ln=1,p=0; 21 while(p<cl) 22 { 23 int k=0; 24 for(int i=cl-ln+1;i<=cl;i++) y[++k]=i; 25 for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln; 26 27 for(int i=1;i<=cl;i++) wr[i]=rk[y[i]]; 28 for(int i=1;i<=m;i++) Rs[i]=0; 29 for(int i=1;i<=cl;i++) Rs[wr[i]]++; 30 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 31 for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i]; 32 33 for(int i=1;i<=cl;i++) wr[i]=rk[i]; 34 for(int i=cl+1;i<=cl+ln;i++) wr[i]=0; 35 p=1;rk[sa[1]]=1; 36 for(int i=2;i<=cl;i++) 37 { 38 if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++; 39 rk[sa[i]]=p; 40 } 41 ln*=2,m=p; 42 } 43 sa[0]=0,rk[0]=0; 44 } 45 46 void get_h() 47 { 48 int k=0,j; 49 for(int i=1;i<=cl;i++) if(rk[i]!=1) 50 { 51 j=sa[rk[i]-1]; 52 if(k) k--; 53 while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++; 54 h[rk[i]]=k; 55 } 56 h[0]=0; 57 } 58 59 int idx(int x) 60 { 61 for(int i=1;i<=n;i++) 62 if((st[i]<=x && x<=ed[i]) || (fst[i]<=x && x<=fed[i])) return i; 63 } 64 65 bool check(int k) 66 { 67 memset(vis,0,sizeof(vis)); 68 for(int i=1;i<=cl;i++) 69 { 70 if(h[i]<k) 71 { 72 int cnt=0; 73 for(int j=1;j<=n;j++) if(vis[j]) cnt++; 74 if(cnt==n) return 1; 75 memset(vis,0,sizeof(vis)); 76 } 77 vis[idx(sa[i])]++; 78 } 79 int cnt=0; 80 for(int j=1;j<=n;j++) if(vis[j]) cnt++; 81 if(cnt==n) return 1; 82 return 0; 83 } 84 85 int main() 86 { 87 freopen("a.in","r",stdin); 88 int T; 89 scanf("%d",&T); 90 while(T--) 91 { 92 scanf("%d",&n); 93 int num=123; 94 cl=0; 95 for(int i=1;i<=n;i++) 96 { 97 scanf("%s",s+1); 98 sl=strlen(s+1); 99 if(i>1) c[++cl]=++num; 100 st[i]=cl+1;for(int j=1;j<=sl;j++) c[++cl]=s[j];ed[i]=cl; 101 c[++cl]=++num; 102 fst[i]=cl+1;for(int j=sl;j>=1;j--) c[++cl]=s[j];fed[i]=cl; 103 } 104 if(n==1) {printf("%d\n",sl);continue;} 105 get_sa(300); 106 get_h(); 107 // for(int i=1;i<=cl;i++) printf("%c",c[i]);printf("\n"); 108 // for(int i=1;i<=cl;i++) printf("%d ",c[i]);printf("\n"); 109 // for(int i=1;i<=cl;i++) printf("%d ",sa[i]);printf("\n"); 110 // for(int i=1;i<=cl;i++) printf("%d ",rk[i]);printf("\n"); 111 // for(int i=1;i<=cl;i++) printf("%d ",h[i]);printf("\n"); 112 int l=0,r=cl,mid; 113 while(l<r) 114 { 115 mid=(l+r+1)/2; 116 if(check(mid)) l=mid; 117 else r=mid-1; 118 } 119 printf("%d\n",l); 120 } 121 return 0; 122 }
这一题我曾经用kmp暴力水过。。贴一下代码
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<algorithm> 7 using namespace std; 8 9 const int N=110,Inf=(int)1e9; 10 char s[N][N],c1[N],c2[N]; 11 int n,id,cl,l[N],nt[N],nxt[N]; 12 13 void kmp_nt() 14 { 15 nt[1]=0; 16 for(int i=2,p=0;i<=cl;i++) 17 { 18 while(c1[p+1]!=c1[i] && p) p=nt[p]; 19 if(c1[p+1]==c1[i]) p++; 20 nt[i]=p; 21 } 22 23 nxt[1]=0; 24 for(int i=2,p=0;i<=cl;i++) 25 { 26 while(c2[p+1]!=c2[i] && p) p=nxt[p]; 27 if(c2[p+1]==c2[i]) p++; 28 nxt[i]=p; 29 } 30 } 31 32 bool kmp_td(int x) 33 { 34 for(int i=1,p=0;i<=l[x];i++) 35 { 36 while(c1[p+1]!=s[x][i] && p) p=nt[p]; 37 if(c1[p+1]==s[x][i]) p++; 38 if(p==cl) return 1; 39 } 40 for(int i=1,p=0;i<=l[x];i++) 41 { 42 while(c2[p+1]!=s[x][i] && p) p=nxt[p]; 43 if(c2[p+1]==s[x][i]) p++; 44 // td[i]=p; 45 if(p==cl) return 1; 46 } 47 return 0; 48 } 49 50 bool check(int len) 51 { 52 for(int i=1;i+len-1<=l[id];i++) 53 { 54 cl=0; 55 for(int j=i;j<=i+len-1;j++) 56 c1[++cl]=s[id][j],c2[len-cl+1]=s[id][j]; 57 kmp_nt(); 58 bool bk=1; 59 for(int j=1;j<=n;j++) 60 if(!kmp_td(j)) {bk=0;break;} 61 if(bk) return 1; 62 } 63 } 64 65 int main() 66 { 67 freopen("a.in","r",stdin); 68 freopen("vio.out","w",stdout); 69 int T; 70 scanf("%d",&T); 71 while(T--) 72 { 73 scanf("%d",&n); 74 int ll=0,rr=Inf,mid; 75 for(int i=1;i<=n;i++) 76 { 77 scanf("%s",s[i]+1); 78 l[i]=strlen(s[i]+1); 79 if(l[i]<rr) rr=l[i],id=i; 80 } 81 while(ll<rr) 82 { 83 mid=(ll+rr+1)/2; 84 if(check(mid)) ll=mid; 85 else rr=mid-1; 86 } 87 printf("%d\n",ll); 88 } 89 return 0; 90 }
以上是关于poj1226-出现或反转后出现在每个串的最长公共子串后缀数组的主要内容,如果未能解决你的问题,请参考以下文章