POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]
Posted ymzjj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]相关的知识,希望对你有一定的参考价值。
题目:http://poj.org/problem?id=3294
Life Forms
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 18549 | Accepted: 5454 |
Description
You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, ears, eyebrows and the like. A few bear no human resemblance; these typically have geometric or amorphous shapes like cubes, oil slicks or clouds of dust.
The answer is given in the 146th episode of Star Trek - The Next Generation, titled The Chase. It turns out that in the vast majority of the quadrant‘s life forms ended up with a large fragment of common DNA.
Given the DNA sequences of several life forms represented as strings of letters, you are to find the longest substring that is shared by more than half of them.
Input
Standard input contains several test cases. Each test case begins with 1 ≤ n ≤ 100, the number of life forms. n lines follow; each contains a string of lower case letters representing the DNA sequence of a life form. Each DNA sequence contains at least one and not more than 1000 letters. A line containing 0 follows the last test case.
Output
For each test case, output the longest string or strings shared by more than half of the life forms. If there are many, output all of them in alphabetical order. If there is no solution with at least one letter, output "?". Leave an empty line between test cases.
Sample Input
3 abcdefg bcdefgh cdefghi 3 xxx yyy zzz 0
Sample Output
bcdefg cdefgh ?
Source
题意概括:
给出 N 个字符串,求其中出现次数超过 N/2 次的最长公共子串,如果有多种输出多种。
解题思路:
做法依然是二分答案长度,关键在于判断条件有两个:
①出现次数是否大于 N/2,这个通过height分组,统计一下即可。
②当前所枚举的子串不仅要求不能重叠,而且要满足来源于原本不同的字符串(因为合并了所有字符串,所以以原来字符串分区,判断两个子串要在不同区)
二分不重叠相同子串的加强版,网上很多版本都是暴力 O( n ) 判断子串是否来自不同串的,复杂度有点爆炸。
这道题复杂度的优化关键在于优化这个判断条件。
有个技巧:合并字符串时在中间加入分隔标志,后面通过 O(1) 标记即可判断是否满足区间要求。
输出子串的话,只要保存满足条件的 sa 即可。
AC code:
1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <vector> 5 #include <cstdio> 6 #include <cstring> 7 #include <string> 8 #include <iostream> 9 #include <algorithm> 10 #define INF 0x3f3f3f3f 11 #define LL long long 12 #define inc(i, j, k) for(int i = j; i <= k ; i++) 13 #define mem(i, j) memset(i, j, sizeof(i)) 14 #define gcd(i, j) __gcd(i, j) 15 #define F(x) ((x)/3+((x)%3==1?0:tb)) 16 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) 17 using namespace std; 18 const int MAXN = 3e5+10; 19 const int maxn = 3e5+10; 20 int r[MAXN]; 21 int wa[MAXN], wb[MAXN], wv[MAXN], tmp[MAXN]; 22 int sa[MAXN]; //index range 1~n value range 0~n-1 23 int cmp(int *r, int a, int b, int l) 24 { 25 return r[a] == r[b] && r[a + l] == r[b + l]; 26 } 27 28 void da(int *r, int *sa, int n, int m) 29 { 30 int i, j, p, *x = wa, *y = wb, *ws = tmp; 31 for (i = 0; i < m; i++) ws[i] = 0; 32 for (i = 0; i < n; i++) ws[x[i] = r[i]]++; 33 for (i = 1; i < m; i++) ws[i] += ws[i - 1]; 34 for (i = n - 1; i >= 0; i--) sa[--ws[x[i]]] = i; 35 for (j = 1, p = 1; p < n; j *= 2, m = p) 36 { 37 for (p = 0, i = n - j; i < n; i++) y[p++] = i; 38 for (i = 0; i < n; i++) 39 if (sa[i] >= j) y[p++] = sa[i] - j; 40 for (i = 0; i < n; i++) wv[i] = x[y[i]]; 41 for (i = 0; i < m; i++) ws[i] = 0; 42 for (i = 0; i < n; i++) ws[wv[i]]++; 43 for (i = 1; i < m; i++) ws[i] += ws[i - 1]; 44 for (i = n - 1; i >= 0; i--) sa[--ws[wv[i]]] = y[i]; 45 for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; i++) 46 x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; 47 } 48 } 49 50 int Rank[MAXN]; //index range 0~n-1 value range 1~n 51 int height[MAXN]; //index from 1 (height[1] = 0) 52 void calheight(int *r, int *sa, int n) 53 { 54 int i, j, k = 0; 55 for (i = 1; i <= n; ++i) Rank[sa[i]] = i; 56 for (i = 0; i < n; height[Rank[i++]] = k) 57 for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; ++k); 58 return; 59 } 60 61 int N; 62 string tp; 63 vector<int>ans_id; 64 int f[MAXN], kase; 65 66 bool check(int limit, int n, int len) 67 { 68 bool flag = false; 69 int cnt = 1; 70 ans_id.clear(); 71 f[sa[1]/len] = kase; 72 for(int i = 1; i <= n; i++){ 73 if(height[i] < limit){ //按height分组 74 f[sa[i]/len] = ++kase; //给区间标记上组的标号 75 cnt = 1; 76 } 77 else{ 78 if(f[sa[i]/len] != kase){ //判断一组中是否有相同区间 79 f[sa[i]/len] = kase; 80 if(cnt>=0) cnt++; 81 if(cnt > N/2){ 82 flag = true; 83 ans_id.push_back(sa[i]); 84 cnt = -1; 85 } 86 } 87 } 88 } 89 return flag; 90 } 91 92 int main() 93 { 94 bool book = false; 95 int ssize, n_len = 0, ans; 96 while(~scanf("%d", &N) && N){ 97 n_len = 0; 98 kase = 1; 99 ans = 0; 100 for(int i = 1; i <= N; i++){ 101 cin >> tp; 102 ssize = tp.size(); 103 for(int k = 0; k < ssize; k++){ 104 r[n_len++] = tp[k]+100; 105 } 106 r[n_len++] = i; //作分隔标记 107 } 108 n_len--; 109 r[n_len] = 0; 110 111 da(r, sa, n_len+1, 277); 112 calheight(r, sa, n_len); 113 114 int L = 0, R = ssize+1, mid; 115 while(L <= R){ 116 mid = (L+R)>>1; 117 if(check(mid, n_len, ssize+1)){ 118 L = mid+1; 119 ans = mid; 120 } 121 else R = mid-1; 122 } 123 check(ans, n_len, ssize+1); 124 125 if(book) puts(""); 126 if(ans == 0) puts("?"); 127 else{ 128 int len = ans_id.size(); 129 // printf("%d ", len); 130 for(int i = 0; i < len; i++){ 131 for(int k = ans_id[i]; k-ans_id[i]+1 <= ans; k++){ 132 printf("%c", r[k]-100); 133 } 134 puts(""); 135 } 136 } 137 if(!book) book = true; 138 } 139 return 0; 140 }
以上是关于POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]的主要内容,如果未能解决你的问题,请参考以下文章