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
Waterloo Local Contest, 2006.9.30
題意概括:
給出 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\n", 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 }422ms 3300k
POJ 3294 Life Forms [最長公共子串加強版 後綴數組 && 二分]