题目: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:

 #include <set>
#include <map>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
#define LL long long
#define inc(i, j, k) for(int i = j; i <= k ; i++)
#define mem(i, j) memset(i, j, sizeof(i))
#define gcd(i, j) __gcd(i, j)
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
using namespace std;
const int MAXN = 3e5+;
const int maxn = 3e5+;
int r[MAXN];
int wa[MAXN], wb[MAXN], wv[MAXN], tmp[MAXN];
int sa[MAXN]; //index range 1~n value range 0~n-1
int cmp(int *r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
} void da(int *r, int *sa, int n, int m)
{
int i, j, p, *x = wa, *y = wb, *ws = tmp;
for (i = ; i < m; i++) ws[i] = ;
for (i = ; i < n; i++) ws[x[i] = r[i]]++;
for (i = ; i < m; i++) ws[i] += ws[i - ];
for (i = n - ; i >= ; i--) sa[--ws[x[i]]] = i;
for (j = , p = ; p < n; j *= , m = p)
{
for (p = , i = n - j; i < n; i++) y[p++] = i;
for (i = ; i < n; i++)
if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = ; i < n; i++) wv[i] = x[y[i]];
for (i = ; i < m; i++) ws[i] = ;
for (i = ; i < n; i++) ws[wv[i]]++;
for (i = ; i < m; i++) ws[i] += ws[i - ];
for (i = n - ; i >= ; i--) sa[--ws[wv[i]]] = y[i];
for (swap(x, y), p = , x[sa[]] = , i = ; i < n; i++)
x[sa[i]] = cmp(y, sa[i - ], sa[i], j) ? p - : p++;
}
} int Rank[MAXN]; //index range 0~n-1 value range 1~n
int height[MAXN]; //index from 1 (height[1] = 0)
void calheight(int *r, int *sa, int n)
{
int i, j, k = ;
for (i = ; i <= n; ++i) Rank[sa[i]] = i;
for (i = ; i < n; height[Rank[i++]] = k)
for (k ? k-- : , j = sa[Rank[i] - ]; r[i + k] == r[j + k]; ++k);
return;
} int N;
string tp;
vector<int>ans_id;
int f[MAXN], kase; bool check(int limit, int n, int len)
{
bool flag = false;
int cnt = ;
ans_id.clear();
f[sa[]/len] = kase;
for(int i = ; i <= n; i++){
if(height[i] < limit){ //按height分组
f[sa[i]/len] = ++kase; //给区间标记上组的标号
cnt = ;
}
else{
if(f[sa[i]/len] != kase){ //判断一组中是否有相同区间
f[sa[i]/len] = kase;
if(cnt>=) cnt++;
if(cnt > N/){
flag = true;
ans_id.push_back(sa[i]);
cnt = -;
}
}
}
}
return flag;
} int main()
{
bool book = false;
int ssize, n_len = , ans;
while(~scanf("%d", &N) && N){
n_len = ;
kase = ;
ans = ;
for(int i = ; i <= N; i++){
cin >> tp;
ssize = tp.size();
for(int k = ; k < ssize; k++){
r[n_len++] = tp[k]+;
}
r[n_len++] = i; //作分隔标记
}
n_len--;
r[n_len] = ; da(r, sa, n_len+, );
calheight(r, sa, n_len); int L = , R = ssize+, mid;
while(L <= R){
mid = (L+R)>>;
if(check(mid, n_len, ssize+)){
L = mid+;
ans = mid;
}
else R = mid-;
}
check(ans, n_len, ssize+); if(book) puts("");
if(ans == ) puts("?");
else{
int len = ans_id.size();
// printf("%d\n", len);
for(int i = ; i < len; i++){
for(int k = ans_id[i]; k-ans_id[i]+ <= ans; k++){
printf("%c", r[k]-);
}
puts("");
}
}
if(!book) book = true;
}
return ;
}

422ms 3300k

POJ 3294 Life Forms [最长公共子串加强版 后缀数组 && 二分]的更多相关文章

  1. cogs249 最长公共子串(后缀数组 二分答案

    http://cogs.pro:8080/cogs/problem/problem.php?pid=pxXNxQVqP 题意:给m个单词,让求最长公共子串的长度. 思路:先把所有单词合并成一个串(假设 ...

  2. 【poj1226-出现或反转后出现在每个串的最长公共子串】后缀数组

    题意:求n个串的最长公共子串,子串出现在一个串中可以是它的反转串出现.总长<=10^4. 题解: 对于每个串,把反转串也连进去.二分长度,分组,判断每个组. #include<cstdio ...

  3. 【poj3294-不小于k个字符串中最长公共子串】后缀数组

    1.注意每两个串之间的连接符要不一样. 2.分组的时候要注意最后一组啊!又漏了! 3.开数组要考虑连接符的数量.100010是不够的至少要101000. #include<cstdio> ...

  4. POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)+后缀数组模板

    Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7586   Accepted: 3448 Cas ...

  5. BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案

    BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案 Description          给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单 ...

  6. poj 1458 Common Subsequence_最长公共子串

    题意:略 求最长公共子串 #include<iostream> #include<cstdio> #include<string> using namespace ...

  7. SPOJ1811最长公共子串问题(后缀自动机)

    题目:http://www.spoj.com/problems/LCS/ 题意:给两个串A和B,求这两个串的最长公共子串. 分析:其实本题用后缀数组的DC3已经能很好的解决,这里我们来说说利用后缀自动 ...

  8. 【wikioi】3160 最长公共子串(后缀自动机)

    http://codevs.cn/problem/3160/ sam的裸题...(之前写了spoj上另一题sam的题目,但是spoj被卡评测现在还没评测完QAQ打算写那题题解时再来详细介绍sam的.. ...

  9. CODE【VS】3160 最长公共子串 (后缀自动机)

    3160 最长公共子串 题目描述 Description 给出两个由小写字母组成的字符串,求它们的最长公共子串的长度. 输入描述 Input Description 读入两个字符串 输出描述 Outp ...

随机推荐

  1. 哪个类可用于处理 Unicode?

    A. InputStreanReader的构造函数: InputStreamReader(InputStream in)          创建一个使用默认字符集的 InputStreamReader ...

  2. Hibernate的一对多实例

    一对多在现实生活中很常见,今天做了个Hibernate的一对多的实例,也是个入门过程,写下来跟大家分享. 最重要的是xml配置文件,之前因为把英文"(引号)错误的复制成中文的“”(引号),导 ...

  3. xamarin.Android ImageView 异步加载网络图片

    /// <summary> /// 异步获取文件流 /// </summary> /// <param name="url"></para ...

  4. git push远程仓库时报错:fatal: remote origin already exists. (已解决)

    在做远程仓库调试阶段,突然发现修改后的项目无法push了: 如果输入$ git remote add origin git@github.com:djqiang(github帐号名)/gitdemo( ...

  5. html中内联元素和块元素的区别、用法以及联系

    昨天用asp.net的BulletedList做一个导航栏,最终该控件形成的html代码是ul列表和a超链接,具体代码如下: <ul id="BulletedList1" s ...

  6. 前端学习之HTML(1)

    HTML标签学习 2018-10-31 记录一下学习的网站 http://www.w3school.com.cn http://www.runoob.com/ <!DOCTYPE html> ...

  7. 超级简单的jquery轮播图demo

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. Spring Boot—14JdbcTemplate

    pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  9. 如何从 GitHub 上下载单个文件夹

    DownGit 好用记得回来点赞(建议***)

  10. rman备份控制文件

    rman备份控制文件分为两种情况. 1.自动备份需要配置configure controlfile autobackup on配置之后,控制文件和spfile会在每次rman备份之后自动备份contr ...