题意: 输入n个序列,求出一个最大长度的字符串,使得它在超过一半的DNA序列中连续出现。如果有多解,按照字典序从小到大输出所有解。

分析:这道题的关键是将多个字符串连接成一个串,方法是用不同的分隔符把所有原串拼接起来。接下来,就可以求这个新串的后缀数组和 height 数组, 然后二分答案,没次只需判断是非有一个长度为p的串在超过一半的串中出现过,判断方法是扫描一遍height数组,把它分成若干段,每当height[i] < p时,开辟一个新段,然后判断之前段是否包含了超过 n/2个原串后缀,那么当前的p值满足条件(注意n = 1时要特判)

详见代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std; const int maxn = ;
const int maxm = ;
char s[maxn*maxm];
int sa[maxn*maxm], t[maxn*maxm], t2[maxn*maxm], c[maxn*maxm]; int N;
void build_sa(int m) {
int* x = t, *y = t2;
for(int i = ; i < m; i++) c[i] = ;
for(int i = ; i < N; i++) c[x[i] = s[i]]++;
for(int i = ; i < m; i++) c[i] += c[i-];
for(int i = N-; i >= ; i--) sa[--c[x[i]]] = i;
for(int k = ; k <= N; k <<= ) {
int p = ;
for(int i = N-k; i < N; i++) y[p++] = i;
for(int i = ; i < N; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
for(int i = ; i < m; i++) c[i] = ;
for(int i = ; i < N; i++) c[x[y[i]]]++;
for(int i = ; i < m; i++) c[i] += c[i-];
for(int i = N-; i >= ; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = ;
x[sa[]] = ;
for(int i = ; i < N; i++)
x[sa[i]] = (y[sa[i-]] == y[sa[i]] && y[sa[i-]+k] == y[sa[i]+k] ? p- :p++);
if(p >= N) break;
m = p;
}
}
int rnk[maxn*maxm], height[maxn*maxm];
void get_height() {
int k = ;
for(int i = ; i < N; i++) rnk[sa[i]] = i;
for(int i = ; i < N; i++) {
if(!rnk[i]) continue;
int j = sa[rnk[i]-];
if(k) k--;
while(s[i+k] == s[j+k]) k++;
height[rnk[i]] = k;
}
} int n;
char s2[maxm];
int sign[maxn];
int mlen;
vector<int> A;
int flag[maxn];
map<char, int> Map;
bool find(int p, vector<int> &A) { //判断当前长度p是否符合要求
memset(flag, , sizeof flag);
bool OK = false;
int cnt = ;
int start = ;
int t = lower_bound(sign, sign+n, sa[start]) - sign;
if(!Map.count(s[sa[start]]))
cnt++;
flag[t] = start;
for(int i = ; i < N; i++) {
if(height[i] >= p) {
t = lower_bound(sign, sign+n, sa[i]) - sign;
if(!Map.count(s[sa[i]]) && flag[t] < start)
cnt++;
flag[t] = i;
if(i == N- && cnt > n/){
OK = true;
A.push_back(sa[start]);
}
}
else {
if(cnt > n/) {
OK = true;
A.push_back(sa[start]);
}
cnt = ;
start = i;
int t = lower_bound(sign, sign+n, sa[start]) - sign;
if(!Map.count(s[sa[start]]))
cnt++;
flag[t] = start;
}
}
return OK;
}
int cnt;
char gen_sign() { //生成分隔符并记录
int i = ;
for(; i < ; i++) if(!Map.count(i) && (i < 'a' || i > 'z')) break;
Map[i] = ++cnt;
return i;
}
int main() {
int tt = ;
while(scanf("%d", &n) == && n) {
if(tt++) puts("");
if(n == ) {
scanf("%s", s);
printf("%s\n", s);
continue;
}
cnt = ;
Map.clear();
N = ;
for(int i = ; i < n; i++) {
scanf("%s", s2);
strcpy(s+N, s2);
N += strlen(s2);
s[N++] = gen_sign();
sign[i] = N-;
}
s[N] = '\0';
//cout << s <<endl;
//for(int i = 0; i < n; i++) cout<< sign[i] <<endl;
build_sa();
get_height();
//for(int i = 0; i < N; i++) printf("%d ", sa[i]);
//puts("");
//for(int i = 0; i < N; i++) printf("%d ", height[i]);
//puts("");
mlen = ;
int L = , R = N-;
A.clear();
vector<int> B;
while(R >= L) {
int M = L + (R-L+)/;
B.clear();
if(find(M, B)) {
mlen = M;
A = B;
L = M+;
}
else R = M-;
} if(A.size() == ) printf("?\n");
for(int i = ; i < A.size(); i++) {
for(int j = ; j < mlen; j++) printf("%c", s[A[i]+j]);
printf("\n");
}
}
}

UVA 11107 Life Forms——(多字符串的最长公共子序列,后缀数组+LCP)的更多相关文章

  1. Python-求解两个字符串的最长公共子序列

    一.问题描述 给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence).比如字符串1:BDCABA:字符串2:ABCBDAB.则这两个字符串的最长公共子序列长 ...

  2. C++求解汉字字符串的最长公共子序列 动态规划

        近期,我在网上看了一些动态规划求字符串最长公共子序列的代码.可是无一例外都是处理英文字符串,当处理汉字字符串时.常常会出现乱码或者不对的情况. 我对代码进行了改动.使用wchar_t类型存储字 ...

  3. UVA - 11475 Extend to Palindrome —— 字符串哈希 or KMP or 后缀数组

    题目链接:https://vjudge.net/problem/UVA-11475 题意: 给出一个字符串,问在该字符串后面至少添加几个字符,使得其成为回文串,并输出该回文串. 题解: 实际上是求该字 ...

  4. (字符串)最长公共子序列(Longest-Common-Subsequence,LCS)

    问题: 最长公共子序列就是寻找两个给定序列的子序列,该子序列在两个序列中以相同的顺序出现,但是不必要是连续的. 例如序列X=ABCBDAB,Y=BDCABA.序列BCA是X和Y的一个公共子序列,但是不 ...

  5. Atcoder F - LCS (DP-最长公共子序列,输出字符串)

    F - LCS Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement You are gi ...

  6. uva 11107 Life Forms

    题意:给你N个串,求一个串在大于等于N/2的模板串中连续出现.如果有多解按字典序最小输出. 白书模板题.二分答案+合并模板串成一个新串,扫秒新串的height数组. 考查后缀数组+LCP #inclu ...

  7. POJ 3294 UVA 11107 Life Forms 后缀数组

    相同的题目,输出格式有区别. 给定n个字符串,求最长的子串,使得它同时出现在一半以上的串中. 不熟悉后缀数组的童鞋建议先去看一看如何用后缀数组计算两个字符串的最长公共子串 Ural1517 这道题的思 ...

  8. poj2774 后缀数组2个字符串的最长公共子串

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 26601   Accepted: 10 ...

  9. POJ 3080 Blue Jeans (多个字符串的最长公共序列,暴力比较)

    题意:给出m个字符串,找出其中的最长公共子序列,如果相同长度的有多个,输出按字母排序中的第一个. 思路:数据小,因此枚举第一个字符串的所有子字符串s,再一个个比较,是否为其它字符串的字串.判断是否为字 ...

随机推荐

  1. SpingBoot myBatis neo4j整合项目案例

    此项目为spring boot - myBatis - neo4j数据库整合项目. 有增删改查(节点关系).动态分页条件排序等一些示例. git下载地址:git clone https://githu ...

  2. Springboot项目下mybatis报错:Invalid bound statement (not found)

    mybatis报错:Invalid bound statement (not found)的原因很多,但是正如报错提示一样,找不到xml中的sql语句,报错的情况分为三种: 第一种:语法错误 Java ...

  3. AJAX之再升级版PJAX

    前几天在一个大神群里提到ajax优化选项卡功能的方法上,有位低调的大神默默得打出:了解一下pjax,好奇心的驱使下,我具体查了一下pjax,不一般啊,ax结合pushState和ajax技术, 不需要 ...

  4. 【机器学习PAI实战】—— 玩转人工智能之利用GAN自动生成二次元头像

    前言 深度学习作为人工智能的重要手段,迎来了爆发,在NLP.CV.物联网.无人机等多个领域都发挥了非常重要的作用.最近几年,各种深度学习算法层出不穷, Generative Adverarial Ne ...

  5. TZOJ4777: 方格取数

    4777: 方格取数  Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByteTotal Submit: 11       ...

  6. 关于 SSD 的接口和相关名词(2019-09-10)

    关于 SSD 的接口和相关名词 了解了很多天的 SSD,太多的名词. 先记录一下. SATA MSATA M2 NVME NGFF U2 TODO: 后续收集相关信息.

  7. 总结 ESP8266 RTOS 开发环境搭建

    总结 ESP8266 RTOS 开发环境搭建 仔细看官方文档. 必须一步一步操作. 不要想当然,以为 make 就可以. 忽略编译警告,除非是错误. 工具链必须使用官方提供的. 多看看 Issues ...

  8. vue-cnodejs

    感谢那些无私开源的程序员,你们是最可爱的人儿~~~~ //根app app.js <template> <div id="app"> <v-heade ...

  9. PHPCMS快速建站系列之pc:get标签的应用

    GET标签使用方式如下: {pc:get sql="SELECT * FROM phpcms_member" cache="3600" page="$ ...

  10. MySQL主备模式的数据一致性解决方案

     根据阿里交易型业务的特点,以及在双十一这样业内罕有的需求推动下,我们在官方的MySQL基础上增加了非常多实用的功能.性能补丁.而在使用MySQL的过程中,数据一致性是绕不开的话题之一.本文主要从阿里 ...