poj 3294 Life Forms
2015-02-17 12:52
197 查看
题意
输入n个DNA序列,你的任务是求出一个长度最大的字符串,使得超过一半的DNA序列中连续出现。如果有多解,按照字典序从小到大输出所有解。
解法
这是一道经典的题目,解法有很多。首先用不同的分隔字符把所有输入的字符串连接在一起。求这个新串的后缀数组和height数组,然后二分答案,每次只需要判断是否有一个长度为p的串在超过一半的串中连续出现。方法是把height分为若干段,每当height小于p时开辟一个新段,每一段开头p个字符都相同。只要某一段中超过n/2个原串的后缀,p就满足条件。
代码
输入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; }
相关文章推荐
- poj 3294 Life Forms(后缀数组)
- poj 3294 Life Forms (后缀数组)
- 【POJ】3294 Life Forms 【后缀数组——求在超过一半串中出现的最长串】
- POJ 3294 Life Forms(后缀数组)
- POJ 3294 Life Forms
- poj 3294 Life Forms(不小于k 个字符串中的最长子串)
- [后缀数组+二分] poj 3294 Life Forms
- POJ 3294 Life Forms(后缀数组+二分)
- poj 3294 Life Forms(后缀数组+二分)
- POJ 3294 Life Forms
- poj-3294-Life Forms
- 【POJ】3294 Life Forms
- POJ 3294 Life Forms(后缀数组)
- POJ 3294 Life Forms (后缀数组)
- POJ3294---Life Forms(后缀数组,二分+给后缀分组)
- poj_3294 Life Forms(后缀数组+二分)
- poj 3294 Life Forms
- POJ 题目3294Life Forms(后缀数组求超过k个的串的最长公共子串)
- POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)
- 字符串(后缀数组):POJ 3294 Life Forms