UVA 11107 Life Forms——(多字符串的最长公共子序列,后缀数组+LCP)
题意: 输入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)的更多相关文章
- Python-求解两个字符串的最长公共子序列
一.问题描述 给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence).比如字符串1:BDCABA:字符串2:ABCBDAB.则这两个字符串的最长公共子序列长 ...
- C++求解汉字字符串的最长公共子序列 动态规划
近期,我在网上看了一些动态规划求字符串最长公共子序列的代码.可是无一例外都是处理英文字符串,当处理汉字字符串时.常常会出现乱码或者不对的情况. 我对代码进行了改动.使用wchar_t类型存储字 ...
- UVA - 11475 Extend to Palindrome —— 字符串哈希 or KMP or 后缀数组
题目链接:https://vjudge.net/problem/UVA-11475 题意: 给出一个字符串,问在该字符串后面至少添加几个字符,使得其成为回文串,并输出该回文串. 题解: 实际上是求该字 ...
- (字符串)最长公共子序列(Longest-Common-Subsequence,LCS)
问题: 最长公共子序列就是寻找两个给定序列的子序列,该子序列在两个序列中以相同的顺序出现,但是不必要是连续的. 例如序列X=ABCBDAB,Y=BDCABA.序列BCA是X和Y的一个公共子序列,但是不 ...
- Atcoder F - LCS (DP-最长公共子序列,输出字符串)
F - LCS Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement You are gi ...
- uva 11107 Life Forms
题意:给你N个串,求一个串在大于等于N/2的模板串中连续出现.如果有多解按字典序最小输出. 白书模板题.二分答案+合并模板串成一个新串,扫秒新串的height数组. 考查后缀数组+LCP #inclu ...
- POJ 3294 UVA 11107 Life Forms 后缀数组
相同的题目,输出格式有区别. 给定n个字符串,求最长的子串,使得它同时出现在一半以上的串中. 不熟悉后缀数组的童鞋建议先去看一看如何用后缀数组计算两个字符串的最长公共子串 Ural1517 这道题的思 ...
- poj2774 后缀数组2个字符串的最长公共子串
Long Long Message Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 26601 Accepted: 10 ...
- POJ 3080 Blue Jeans (多个字符串的最长公共序列,暴力比较)
题意:给出m个字符串,找出其中的最长公共子序列,如果相同长度的有多个,输出按字母排序中的第一个. 思路:数据小,因此枚举第一个字符串的所有子字符串s,再一个个比较,是否为其它字符串的字串.判断是否为字 ...
随机推荐
- mysql更改密码
mysql command line client输入密码以后闪退问题的解决: 网上搜到的解决办法(my.ini文件之类的修改对我都没有起到作用).. 所以觉得是自己密码的问题,因为许久不用这个软件了 ...
- Linux常用命令2 权限管理命令
1.权限管理命令:chmod 上面图片中的ugoa与rwx并不是一个命令,而是不同选项 u 所有者 g 所属组 o 其他人 a 所有人 r 读取权限 w写入权限 x 执行权限 chmod u+x ...
- BootstrapValidation一些tips
BootstrapValidation一些tips:1. callback的用法 如果你有一些特别的检查需要,比如两个元素必需有一个有值,你可以在两个元素上加上callback,例:sel和cb必需有 ...
- iOS block 用法
1.定义Block /* 回传void ,参数也是void 的block*/ void (^blockReturningVoidWithVoidArgument)( void ); /* 回传整数,两 ...
- 一个基于Asterisk构建的VOIP应用软件:Elastix介绍
Elastix 是一种应用软件,它整合了适用于那些基于 Asterisk 的 PBX 的最好工具,并将它们集成为单一的.易用的接口.同时,它增加了自己的工具集,以及允许创建第三方模块来使 Elasti ...
- Directx11教程(64) tessellation学习(6)-PN Triangles
原文:Directx11教程(64) tessellation学习(6)-PN Triangles 前面我们用tessellation细分三角形或者四边形,产生的细分点都是在三角形或四边形 ...
- 17个你必须牢记的Win10快捷键
电脑初学者掌握了盲打技术,可以提高录入速度:游戏玩家掌握了快捷键,可以在瞬息百变的对战中提高生存的机会:而Windows玩家掌握了快捷键,不但可以提高电脑操作速度,更能享受到初级玩家望着你那仰慕的眼神 ...
- day20 BBS前奏
Django的ORM操作 1.FK 2.M2M 3.O2O 4.CURD 5.aggregate聚合 6.F 7.Q FORM表单 Django form model form views介绍 adm ...
- CSDN编程挑战——《-3+1》
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/user_longling/article/details/24674033 -3+1 题目详情: 有 ...
- Unicode与FFFE(记一个蛋疼的项目)
好久没更新博客了. 近期忙着一个项目, 还要应付各种考试就顾不上博客了. 今天遇到了一个蛋疼的问题, 通过BLE4.0与蓝牙外设通信. 按照客户给的协议文档发送的数据, 可是外设不能正确识别.折腾了一 ...