UVA-11107 Life Forms(求出现K次的子串,后缀数组+二分答案)
Posted songorz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVA-11107 Life Forms(求出现K次的子串,后缀数组+二分答案)相关的知识,希望对你有一定的参考价值。
题解:
题意:
输入n个DNA序列,你的任务是求出一个长度最大的字符串,使得它在超过一半的DNA序列中出现。如果有多解,按照字典序从小到大输入所有解。
把n个DNA序列拼在一起,中间用没有出现过的字符分割。然后求出height数组。
二分满足要求的字符串长度L,然后判断是否可行。
判断可行:
分组方法,如果某一组(段)有超过n/2的DNA串(是对应的输入的DNA串要有n/2个),则可行。
参考代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=100*2+10; 4 const int maxm=10000000*2; 5 int idx[maxm],n; 6 struct SuffixArray{ 7 int s[maxm]; 8 int sa[maxm],height[maxm],rank[maxm],n; 9 int t[maxm*2],t2[maxm*2]; 10 long long cnt[maxm]; 11 void clear(){n=0;} 12 void build_sa(int m) 13 { 14 int i,*x=t,*y=t2; 15 for(i=0;i<m;i++) cnt[i]=0; 16 for(i=0;i<n;i++) cnt[x[i]=s[i]]++; 17 for(i=1;i<m;i++) cnt[i]+=cnt[i-1]; 18 for(i=n-1;i>=0;i--) sa[--cnt[x[i]]]=i; 19 for(int k=1,p=0;k<n;k <<=1) 20 { 21 p=0; 22 for(i=n-k;i<n;i++) y[p++]=i; 23 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 24 for(i=0;i<m;i++) cnt[i]=0; 25 for(i=0;i<n;i++) cnt[x[y[i]]]++; 26 for(i=1;i<m;i++) cnt[i]+=cnt[i-1]; 27 for(i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]]=y[i]; 28 swap(x,y); 29 p=1;x[sa[0]]=0; 30 for(i=1;i<n;i++) 31 { 32 if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]) x[sa[i]]=p-1; 33 else x[sa[i]]=p++; 34 } 35 if(p>=n) break; 36 m=p; 37 } 38 } 39 void build_height() 40 { 41 int k=0; 42 for(int i=0;i<n;i++) rank[sa[i]]=i; 43 for(int i=0;i<n;i++) 44 { 45 if(k) k--; 46 if(!rank[i]) continue; 47 int j=sa[rank[i]-1]; 48 while(s[i+k]==s[j+k]) k++; 49 height[rank[i]]=k; 50 } 51 } 52 } SA; 53 54 inline void add(int ch,int id) 55 { 56 idx[SA.n]=id; 57 SA.s[SA.n++]=ch;//// 58 } 59 60 int flag[maxn]; 61 inline int check(int ans) 62 { 63 int check_clock=1; 64 memset(flag,0,sizeof(flag)); 65 for(int i=1;i<SA.n;i++) 66 { 67 if(SA.height[i]>=ans) 68 { 69 flag[idx[SA.sa[i]]]=check_clock; 70 flag[idx[SA.sa[i-1]]]=check_clock; 71 } 72 else 73 { 74 int cnt=0; 75 for(int j=0;j<n;j++) if(flag[j]==check_clock) cnt++; 76 if(cnt>n/2) return true; 77 flag[idx[SA.sa[i]]]=++check_clock; 78 } 79 } 80 return false; 81 } 82 83 inline void print_ans(int l,int r) 84 { 85 for(int i=l;i<=r;i++) printf("%c",SA.s[i]+‘a‘-1); 86 printf(" "); 87 } 88 89 inline void print(int ans) 90 { 91 int check_clock=1; 92 memset(flag,0,sizeof(flag)); 93 for(int i=1;i<SA.n;i++) 94 { 95 if(SA.height[i]>=ans) 96 { 97 flag[idx[SA.sa[i]]]=check_clock; 98 flag[idx[SA.sa[i-1]]]=check_clock; 99 } 100 else 101 { 102 int cnt=0; 103 for(int j=0;j<n;j++) if(flag[j]==check_clock) cnt++; 104 if(cnt>n/2) print_ans(SA.sa[i-1],SA.sa[i-1]+ans-1); 105 flag[idx[SA.sa[i]]]=++check_clock; 106 } 107 } 108 } 109 char s[1000+10]; 110 int kase=0; 111 int main() 112 { 113 int maxlen; 114 while(scanf("%d",&n)==1&&n) 115 { 116 if(kase++) printf(" "); 117 SA.clear(); 118 maxlen=0; 119 for(int i=0;i<n;i++) 120 { 121 scanf("%s",s); 122 int l=strlen(s); 123 maxlen=max(maxlen,l); 124 for(int j=0;j<l;j++) add(s[j]-‘a‘+1,i); 125 add(100+i,n); 126 } 127 if(n==1) {printf("%s ",s);continue;} 128 SA.build_sa(101+n); 129 SA.build_height(); 130 int l=1,r=maxlen,ans=0; 131 while(l<=r) 132 { 133 int mid=((l+r)>>1); 134 if(check(mid)) ans=mid,l=mid+1; 135 else r=mid-1; 136 } 137 if(ans) print(ans); 138 else printf("? "); 139 } 140 return 0; 141 }
以上是关于UVA-11107 Life Forms(求出现K次的子串,后缀数组+二分答案)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 3294 UVA 11107 Life Forms 后缀数组
UVa 11107 生命的形式(不小于k个字符串中的最长子串)