题目链接:https://vjudge.net/problem/POJ-3294

Life Forms
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 16905   Accepted: 4970

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

  1. 3
  2. abcdefg
  3. bcdefgh
  4. cdefghi
  5. 3
  6. xxx
  7. yyy
  8. zzz
  9. 0

Sample Output

  1. bcdefg
  2. cdefgh
  3.  
  4. ?

Source

题意:

给出n个字符串,问是否存在至少出现于n/2+1个字符串中的公共子串。如果存在,输入长度最大的;如果有多个答案,按字典序输出所有。

题解:

1.将n个字符串拼接在一起,并且相邻两个之间用分隔符隔开,并且分隔符应各异。因此得到新串。

2.求出新串的后缀数组,然后二分公共子串的长度mid:可知当前的mid可将新串的后缀按排名的顺序将其分成若干组,且每一组的最长公共前缀都大于等于mid,于是就在每一组内统计出现了多少个字符串,如果>n/2,即表明当前mid合法,否则不合法,因此可以根据此规则最终求得长度。

3.由于题目还要求按字典序输出所有答案。所以,在求得长度之后,再遍历一遍sa[]数组,并且判断每个分组是否满足要求,若满足,则输出答案。

注意点:

1.每个分隔符应该不一样,如果一样,在求后缀数组的时候就很可能从当前字符串匹配到下一个字符串,而这是不可能的,因为对于每个字符,最多只能匹配到串尾。

2.输出答案时,为了避免同一组内多次输出(每一组对应着一个子串),应该加个标记。

代码如下:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <vector>
  6. #include <cmath>
  7. #include <queue>
  8. #include <stack>
  9. #include <map>
  10. #include <string>
  11. #include <set>
  12. using namespace std;
  13. typedef long long LL;
  14. const int INF = 2e9;
  15. const LL LNF = 9e18;
  16. const int MOD = 1e9+;
  17. const int MAXN = 2e5+;
  18.  
  19. int id[MAXN]; //记录属于哪个字符串
  20. int r[MAXN], sa[MAXN], Rank[MAXN], height[MAXN];
  21. int t1[MAXN], t2[MAXN], c[MAXN];
  22.  
  23. bool 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 str[], int sa[], int Rank[], int height[], int n, int m)
  29. {
  30. n++;
  31. int i, j, p, *x = t1, *y = t2;
  32. for(i = ; i<m; i++) c[i] = ;
  33. for(i = ; i<n; i++) c[x[i] = str[i]]++;
  34. for(i = ; i<m; i++) c[i] += c[i-];
  35. for(i = n-; i>=; i--) sa[--c[x[i]]] = i;
  36. for(j = ; j<=n; j <<= )
  37. {
  38. p = ;
  39. for(i = n-j; i<n; i++) y[p++] = i;
  40. for(i = ; i<n; i++) if(sa[i]>=j) y[p++] = sa[i]-j;
  41.  
  42. for(i = ; i<m; i++) c[i] = ;
  43. for(i = ; i<n; i++) c[x[y[i]]]++;
  44. for(i = ; i<m; i++) c[i] += c[i-];
  45. for(i = n-; i>=; i--) sa[--c[x[y[i]]]] = y[i];
  46.  
  47. swap(x, y);
  48. p = ; x[sa[]] = ;
  49. for(i = ; i<n; i++)
  50. x[sa[i]] = cmp(y, sa[i-], sa[i], j)?p-:p++;
  51.  
  52. if(p>=n) break;
  53. m = p;
  54. }
  55.  
  56. int k = ;
  57. n--;
  58. for(i = ; i<=n; i++) Rank[sa[i]] = i;
  59. for(i = ; i<n; i++)
  60. {
  61. if(k) k--;
  62. j = sa[Rank[i]-];
  63. while(str[i+k]==str[j+k]) k++;
  64. height[Rank[i]] = k;
  65. }
  66. }
  67.  
  68. bool vis[];
  69. bool test(int n, int len, int k)
  70. {
  71. int cnt = ;
  72. memset(vis, false, sizeof(vis));
  73. for(int i = ; i<=len; i++)
  74. {
  75. if(height[i]<k)
  76. {
  77. cnt = ;
  78. memset(vis, false, sizeof(vis));
  79. }
  80. else
  81. {
  82. if(!vis[id[sa[i-]]]) vis[id[sa[i-]]] = true, cnt++;
  83. if(!vis[id[sa[i]]]) vis[id[sa[i]]] = true, cnt++;
  84. if(cnt>n/) return true;
  85. }
  86. }
  87. return false;
  88. }
  89.  
  90. void Print(int n, int len, int k)
  91. {
  92. int cnt = , flag = false;
  93. memset(vis, false, sizeof(vis));
  94. for(int i = ; i<=len; i++)
  95. {
  96. if(height[i]<k)
  97. {
  98. flag = false;
  99. cnt = ;
  100. memset(vis, false, sizeof(vis));
  101. }
  102. else
  103. {
  104. if(!vis[id[sa[i-]]]) vis[id[sa[i-]]] = true, cnt++;
  105. if(!vis[id[sa[i]]]) vis[id[sa[i]]] = true, cnt++;
  106. if(cnt>n/ &&!flag)
  107. {
  108. flag = true; //表明当前组已经输出了
  109. for(int j = sa[i]; j<sa[i]+k; j++)
  110. putchar(r[j]+'a'-);
  111. putchar('\n');
  112. }
  113. }
  114. }
  115. }
  116.  
  117. char str[MAXN];
  118. int main()
  119. {
  120. int n, firCase = false;
  121. while(scanf("%d", &n)&&n)
  122. {
  123. int len = ;
  124. for(int i = ; i<n; i++)
  125. {
  126. scanf("%s", str);
  127. int LEN = strlen(str);
  128. for(int j = ; j<LEN; j++)
  129. {
  130. r[len] = str[j]-'a'+;
  131. id[len++] = i;
  132. }
  133. r[len] = +i; //分隔符要各异
  134. id[len++] = i;
  135. }
  136. r[len] = ;
  137. DA(r,sa,Rank,height,len,);
  138.  
  139. int l = , r = ;
  140. while(l<=r)
  141. {
  142. int mid = (l+r)>>;
  143. if(test(n,len,mid))
  144. l = mid + ;
  145. else
  146. r = mid - ;
  147. }
  148.  
  149. if(firCase) printf("\n");
  150. firCase = true;
  151. if(r==) puts("?");
  152. else Print(n, len, r);
  153. }
  154. }

POJ3294 Life Forms —— 后缀数组 最长公共子串的更多相关文章

  1. POJ 2217 (后缀数组+最长公共子串)

    题目链接: http://poj.org/problem?id=2217 题目大意: 求两个串的最长公共子串,注意子串是连续的,而子序列可以不连续. 解题思路: 后缀数组解法是这类问题的模板解法. 对 ...

  2. POJ-2774-Long Long Message(后缀数组-最长公共子串)

    题意: 给定两个字符串 A 和 B,求最长公共子串. 分析: 字符串的任何一个子串都是这个字符串的某个后缀的前缀. 求 A 和 B 的最长公共子串等价于求 A 的后缀和 B 的后缀的最长公共前缀的最大 ...

  3. [poj 2274]后缀数组+最长公共子串

    题目链接:http://poj.org/problem?id=2774 后缀数组真的太强大了,原本dp是O(nm)的复杂度,在这里只需要O(n+m). 做法:将两个串中间夹一个未出现过的字符接起来,然 ...

  4. CSU1632Repeated Substrings(后缀数组/最长公共前缀)

    题意就是求一个字符串的重复出现(出现次数>=2)的不同子串的个数. 标准解法是后缀数组.最长公共前缀的应用,对于样例aabaab,先将所有后缀排序: aab 3    aabaab 1    a ...

  5. POJ3450 Corporate Identity —— 后缀数组 最长公共子序列

    题目链接:https://vjudge.net/problem/POJ-3450 Corporate Identity Time Limit: 3000MS   Memory Limit: 65536 ...

  6. POJ3294 Life Forms(后缀数组)

    引用罗穗骞论文中的话: 将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中 ...

  7. POJ3415 Common Substrings —— 后缀数组 + 单调栈 公共子串个数

    题目链接:https://vjudge.net/problem/POJ-3415 Common Substrings Time Limit: 5000MS   Memory Limit: 65536K ...

  8. [SPOJ1811]Longest Common Substring 后缀自动机 最长公共子串

    题目链接:http://www.spoj.com/problems/LCS/ 题意如题目,求两个串的最大公共子串LCS. 首先对其中一个字符串A建立SAM,然后用另一个字符串B在上面跑. 用一个变量L ...

  9. POJ 2774 (后缀数组 最长公共字串) Long Long Message

    用一个特殊字符将两个字符串连接起来,然后找最大的height,而且要求这两个相邻的后缀的第一个字符不能在同一个字符串中. #include <cstdio> #include <cs ...

随机推荐

  1. hadoop datanode节点超时时间设置

    datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长. HDFS默认的超时时长为10分 ...

  2. linux中文件描述符

    :: # cat ping.txt PING baidu.com (() bytes of data. bytes from ttl= time=32.1 ms bytes from ttl= tim ...

  3. Android的Framework分析---5 ActivityManager分析

    Android系统开发交流群:484966421 OSHome. 微信公众号:oshome2015 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font ...

  4. vue2.X props 数据传递 实现组件内数据与组件外的数据的双向绑定

    vue2.0 禁止 子组件修改父组件数据 在Vue2中组件的props的数据流动改为了只能单向流动,即只能由组件外(调用组件方)通过组件的DOM属性attribute传递props给组件内,组件内只能 ...

  5. Hessian原理与程序设计

     Hessian是比較经常使用的binary-rpc.性能较高,适合互联网应用.主要使用在普通的webservice 方法调用.交互数据较小的场景中.hessian的数据交互基于http协议,通常he ...

  6. apue学习笔记(第十二章 线程控制)

    本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...

  7. iOS 瀑布流封装

    代码地址如下:http://www.demodashi.com/demo/12284.html 一.效果预览 功能描述:WSLWaterFlowLayout 是在继承于UICollectionView ...

  8. mysql导出导入所有数据库

    导出所有数据库 mysqldump -uroot -p123456 --all-databases > /home/aa.sql 导入所有数据库 mysql -uroot -p123456 &l ...

  9. hdu 2063过山车

    二分匹配 #include<iostream> #include<cstring> using namespace std; int k,m,n; int rem[500+5] ...

  10. python学习(四)字符串学习

    #!/usr/bin/python # 这一节学习的是python中的字符串操作 # 字符串是在Python中作为序列存在的, 其他的序列有列表和元组 # 1. 序列的操作 S = 'Spam' # ...