码迷,mamicode.com
首页 > 编程语言 > 详细

POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]

时间:2019-04-12 01:02:23      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:style   while   bool   ant   com   super   mat   rip   xxx   

题目: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\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 [最长公共子串加强版 后缀数组 && 二分]

标签:style   while   bool   ant   com   super   mat   rip   xxx   

原文地址:https://www.cnblogs.com/ymzjj/p/10693387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!