1. 程式人生 > >POJ 3294 Life Forms [最長公共子串加強版 後綴數組 && 二分]

POJ 3294 Life Forms [最長公共子串加強版 後綴數組 && 二分]

ide any rip pan ref bsp des 次數 letters

題目: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 [最長公共子串加強版 後綴數組 && 二分]