题意: 输入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. mysql全连接

    Oracle数据库支持full join,mysql是不支持full join的,但仍然可以同过左外连接+ union+右外连接实现 SELECT * FROM t1 LEFT JOIN t2 ON ...

  2. 使用 Windows 10 WSL 搭建 ESP8266 编译环境并使用 VSCODE 编程(一)(2019-08-23)

    目录 使用 Windows 10 WSL 搭建 ESP8266 编译环境并使用 VSCODE 编程 安装前准备 安装 ESP8266 工具链 下载 ESP8266 SDK 编译 花絮 使用 Windo ...

  3. Effective C++: 06继承与面向对象设计

    32:确定你的public继承塑模出is-a关系 以C++进行面向对象编程,最重要的一个规则是:public继承表示的是"is-a"(是一种)的关系. 如果令class D以pub ...

  4. 《第一行代码》之——1.Android简介

    Android简介 Android系统架构 (图片源自维基百科) Android大致分为四层架构,五块区域. Linux内核层 Android系统基于Linux2.6,这一层为Android设备的各种 ...

  5. hdu2044 dp

    /* 每一种情况都可以由周围2个点得出 */ #include<stdio.h> int main() { __int64 dp[]; int i,t,l,r; dp[]=; dp[]=; ...

  6. TIJ——Chapter Three:Operators

    Operators 本章节比较简单,所以简单的做一些笔记: 几个要点: 1.When the compiler sees a String followed by a "+" fo ...

  7. Linux下如何切换用户

    切换用户的命令为:su username 从普通用户切换到root用户,还可以使用命令:sudo su 在终端输入exit或logout或使用快捷方式ctrl+d,可以退回到原来用户,其实ctrl+d ...

  8. mysql 中合并查询结果union用法 or、in与union all 的查询效率

    mysql 中合并查询结果union用法 or.in与union all 的查询效率 (2016-05-09 11:18:23) 转载▼ 标签: mysql union or in 分类: mysql ...

  9. Myeclipse 设置默认注释

    windows-->preference-->Java-->Code Style-->Code Templates code-->New Java files ${fil ...

  10. python 实现A*算法

    A*作为最常用的路径搜索算法,值得我们去深刻的研究.路径规划项目.先看一下维基百科给的算法解释:https://en.wikipedia.org/wiki/A*_search_algorithm A ...