poj 3294 Life Forms

来源:互联网 发布:aframe.js 加载obj 编辑:程序博客网 时间:2024/06/10 18:40

题意

输入n个DNA序列,你的任务是求出一个长度最大的字符串,使得超过一半的DNA序列中连续出现。如果有多解,按照字典序从小到大输出所有解。

解法

这是一道经典的题目,解法有很多。首先用不同的分隔字符把所有输入的字符串连接在一起。求这个新串的后缀数组和height数组,然后二分答案,每次只需要判断是否有一个长度为p的串在超过一半的串中连续出现。方法是把height分为若干段,每当height小于p时开辟一个新段,每一段开头p个字符都相同。只要某一段中超过n/2个原串的后缀,p就满足条件。

代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MAX = 110000;int s[MAX];char ss[MAX];bool mark[1010];int n, k, size;int sa[MAX],rank[MAX],height[MAX];int wa[MAX],wb[MAX],wv[MAX],ws[MAX];int len[1010],vis[1010],good[MAX];int cmp(int *r,int a,int b,int l)  {      return r[a] == r[b] && r[a+l] == r[b+l];  }  void fun(int *r, int n, int m){                                    int i,j,p,*x = wa, *y = wb, *t;      for(i = 0; i < m; i ++) ws[i] = 0;      for(i = 0; i < n; i ++) ws[x[i] = r[i]] ++;      for(i = 1; i < m; i ++) ws[i] += ws[i-1];      for(i = n-1; i >= 0; i --) sa[--ws[x[i]]] = i;      for(j = 1, p = 1; p < n; j*=2, m = p){          for(p = 0, i = n-j; i < n; i ++) y[p++] = i;          for(i = 0; i < n; i ++)            if(sa[i] >= j)                y[p++] = sa[i]-j;          for(i = 0; i < n; i ++) wv[i] = x[y[i]];          for(i = 0; i < m; i ++) ws[i] = 0;          for(i = 0; i < n; i ++) ws[wv[i]] ++;          for(i = 1; i < m; i ++) ws[i] += ws[i-1];          for(i = n-1; i >= 0; i--) sa[--ws[wv[i]]] = y[i];                    for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++)              x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;      }  }  void calheight(int *r, int n){     int i, j, k=0;     for(int i=1; i<=n; i++)         rank[sa[i]] = i;           for(int i=0; i<n; i++){       if(k) k--;       int j = sa[rank[i]-1];       while(r[i+k] == r[j+k]) k++;       height[rank[i]] = k;    }   }  bool check(int mid){memset(mark, false, sizeof(mark));int ans = 0, num = 0;for(int i=2; i<=k; i++){if(height[i] >= mid){for(int j=1; j<=size; j++){if(sa[i-1] > len[j-1] && sa[i-1] < len[j]){ans += mark[j] ? 0 : 1;mark[j] = 1;}if(sa[i] > len[j-1] && sa[i] < len[j]){ans += mark[j] ? 0 : 1;mark[j] = 1;}}}else{if(ans > (n >> 1))good[++num] = sa[i-1];ans = 0;memset(mark, false, sizeof(mark));}}if(ans > (n >> 1))good[++num] = sa[k];if(num){good[0] = num;return true;}return false;}int main(){int t = 0; while(scanf("%d",&n) == 1 && n){k = 0, size = 0,len[0] = -1;for(int i=0;i<n;++i){              scanf("%s",ss + k);              for(;ss[k] != '\0';++k) s[k] = ss[k];              s[len[++size] = k++] = 300 + i;        }  s[k-1] = 0;fun(s, k, 400);calheight(s, k-1);if(t++) puts("");good[0] = 0;int l = 1, r = k;while(l <= r){int mid = (l + r) >> 1;if(check(mid)) l = mid + 1;else r = mid -1;}if(l == 1) puts("?");else{for(int i=1; i<=good[0]; i++){for(int j=good[i]; j<good[i]+l-1; j++)printf("%c",s[j]);puts("");}}}return 0;}


0 0