题意: 输入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. JavaScript--漏写var却还能使用标签

    一个漏写var带来的问题: 这个是不标准的写法!不建议使用 但是效果还是出来了,为什么呢? 原因: https://zhidao.baidu.com/question/1637589020484843 ...

  2. 数据库lib7第4题创建存储过程

    1. 传入2个字符串变量,其中,每个字符串是用分号(:)分隔的字串形式, 比如str1=’ab12;ab;cccc;tty’, str2=’1;6sf;8fffff;dd’, 注意,字符串是用户输入的 ...

  3. JVM学习篇章(一)

    熟悉Java开发的同学,应该都知道jvm是什么,---它就是Java虚拟机,今天我们就学习一下:  内存分配原则:  常用的监控命令

  4. python 同名变量引用

  5. 通过DataWorks数据集成归档日志服务数据至MaxCompute进行离线分析

    通过DataWorks归档日志服务数据至MaxCompute 官方指导文档:https://help.aliyun.com/document_detail/68322.html但是会遇到大家在分区上或 ...

  6. java8 各种时间转换方法

    java8 各种时间转换方法 本来按照常理日期时间是一个很简单的东西,只需要根据一个时间戳就可以算出当前的时间了.但这其实只是初级的想法,是因为你的项目还没有到跨时区部署的程度,一旦你的项目要部署到其 ...

  7. CoreData遇见iCloud的那些坑

    尽管苹果把iCloud与CoreData之间的完美配合吹的天花乱坠,但在iOS7之前,想用iCloud同步CoreData数据简直就是噩梦,苹果自己也承认了之前的诸多bug和不稳定性,这让苹果不得不重 ...

  8. 从Kubernetes 1.14 发布,看技术社区演进方向

    Kubernetes 1.14 正式发布已经过去了一段时间,相信你已经从不同渠道看过了各种版本的解读. 不过,相比于代码 Release,马上就要迎来5周岁生日的Kubernetes 项目接下来如何演 ...

  9. Android 系统字体和颜色样式

    Android 字体和颜色 对于能够显示文字的控件(如TextView EditText RadioButton Button CheckBox Chronometer等等),你有时需要控制字体的大小 ...

  10. 荣获“5G MEC优秀商用案例奖”,阿里云边缘计算发力新零售

    4月24日,在中国联通合作伙伴大会的 “5G MEC(Mobile Edge Computing,移动边缘计算)边缘云赋能行业数字化转型”分论坛上,阿里云“基于5G边缘计算的新零售应用案例”荣获201 ...