题目: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. php5.6+apache2.4+linux搭建php环境

    前言 最近突然想搭建个人博客,尽管笔者擅长java-web,但综合各种原因,于是选择了大众化的php+mysql搭建个人博客.对于php,只闻其大名,但从未学过,于是,笔者将从php环境搭建开始,到服 ...

  2. JVM之---垃圾回收

    JVM通过GC来回收堆和方法区中的内存,GC的基本原理首先会找到程序中不再被使用的对象,然后回收这些对象所占用的内存. 1.收集器 通常采用收集器的方式实现GC,主要的收集器有引用计数收集器和跟踪收集 ...

  3. Linux常用指令大全

    2017-03-25   16:35:42 刚开始学习Linux,由于记忆力有限,把平时常用的Linux命令整理出来,以便随时查阅:  linux 基本命令    ls     (list 显示当前目 ...

  4. android调试debug快捷键

       1. [Ctrl+Shift+B]:在当前行设置断点或取消设置的断点.    2. [F11]:调试最后一次执行的程序.    3. [Ctrl+F11]:运行最后一次执行的程序.    4. ...

  5. JavaScript对象遍历属性和值

    原文链接:http://caibaojian.com/javascript-object-3.html 加入你输出来一个对象,但是苦于不知道里面有哪些属性和值,这个时候,你可以通过下面的代码来遍历这个 ...

  6. <Android 基础(二十五)> Frame Animation

    简介 Frame Animation, 逐帧动画,通过定义一系列的Drawable对象来实现动画效果,可以用来作为视图的背景. Frame Animation在代码中体现为AnimationDrawa ...

  7. Spring Boot—03REST请求

    package com.smartmap.sample.ch1.controller.rest; import java.util.List; import org.apache.commons.lo ...

  8. jQuery轮播图(手动点击轮播)

    下面来看看最终做的手动点击轮播效果: 一.原理说明 (1)首先是轮播图的架构,我采用了一个最外边的大div包住两个小div,一个小div里面放四张图片,另一个小div里面放四个数字按钮 (2)对最外边 ...

  9. 【转】用JS完成手机短信验证按键点击事件

    原地址:https://gitee.com/RainVanilla/codes/i7jske4wdogvnb0apmfx571 试了一下,效果还可以,留着备用! <!DOCTYPE html&g ...

  10. 命令模式-实现undo和redo

    这次实验主要是实现多次redo和undo,即程序的撤回和恢复,这里只实现加法的撤回和恢复. 程序的撤回和恢复就是由所使用的软件来记录操作步骤,可以将数据恢复到某个操作状态. 撤回这个指令很常见,Win ...