zoukankan      html  css  js  c++  java
  • 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 }
    422ms 3300k
  • 相关阅读:
    hdu5833----高斯消元
    高斯消元模板
    hdu4462--曼哈顿距离
    卡特兰数应用
    poj3070矩阵快速幂求斐波那契数列
    poj1042
    poj1328
    mvc 请求处理管道
    sql update 代替游标写法
    sql 表字段模糊连接
  • 原文地址:https://www.cnblogs.com/ymzjj/p/10693387.html
Copyright © 2011-2022 走看看