uva11107

题意

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

分析

论文

后缀数组经典题。加深几个关键数组的印象。

和 poj2774 一样,都是要去连接字符串,保证分隔符不能和字符串内的字符相同,且不能重复。

为什么要连接呢?因为求后缀数组实际是对后缀字符串进行排序,那么有公共前缀子串的后缀字符串会尽可能的排在一起,不同的分隔符保证公共子串不会扩散到别的串上。而 height 数组对应的就是相邻 sa 数组的 lcp ( 最长公共前缀 )。根据选择的最大长度 m,可以将连续的且 lcp 长度大于等于 m 的后缀子串分到一组,要去掉那些在同一个原串里的子串,用一个标记数组标记当前字符属于哪个原串。最后统计个数是否大于一半即可。

这种求最大、最小应该想到和二分法有关。

code

#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
const int MAXN = 2e5 + 10;
char s[MAXN];
int sa[MAXN], t[MAXN], t2[MAXN], c[MAXN], n; // n 为 字符串长度 + 1,s[n - 1] = 0 int rnk[MAXN], height[MAXN];
// 构造字符串 s 的后缀数组。每个字符值必须为 0 ~ m-1
void build_sa(int m) {
int i, *x = t, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;
for(i = 1; i < m; i++) c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
for(i = n - k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; i < m; i++) c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}
void getHeight() {
int i, j, k = 0;
for(i = 0; i < n; i++) rnk[sa[i]] = i;
for(i = 0; i < n - 1; i++) {
if(k) k--;
j = sa[rnk[i] - 1];
while(s[i + k] == s[j + k]) {
k++;
}
height[rnk[i]] = k;
}
}
// 保证 s[n-1] = 0 且前面非 0 // 也就是说空串在最前
// sa[0] = n - 1,sa[i] 有效的只有 [1, n-1] ( 因为前面的 n 加 1 了 )表示第 i 位的是谁( 以第几个字符开始的字符串后缀 )
// height[i] 有效的只有 [2, n-1] 表示 lcp(sa[i], sa[i-1]) 最长公共前缀
char s1[MAXN];
int id[MAXN];
int check(int c, int m) {
set<int> S;
S.insert(id[sa[1]]);
for(int i = 2; i < n; i++) {
while(i < n && height[i] >= m) {
S.insert(id[sa[i]]);
i++;
}
if(2 * S.size() > c) return 1;
S.clear();
S.insert(id[sa[i]]);
}
return 0;
}
void print(int c, int m) {
set<int> S;
S.insert(id[sa[1]]);
for(int i = 2; i < n; i++) {
while(i < n && height[i] >= m) {
S.insert(id[sa[i]]);
i++;
}
if(2 * S.size() > c) {
int bgn = sa[i - 1];
for(int j = 0; j < m; j++) {
printf("%c", s[bgn + j]);
}
puts("");
}
S.clear();
S.insert(id[sa[i]]);
}
}
int main() {
int c;
int f = 1;
while(scanf("%d", &c) && c) {
memset(s, 0, sizeof s);
if(!f) puts("");
else f = 0;
int bound = 1;
for(int i = 0; i < c; i++) {
scanf("%s", s1);
int l = strlen(s), l1 = strlen(s1);
for(int j = 0; j < l1; j++) {
s[j + l] = s1[j];
id[j + l] = i;
}
if(bound == 97) bound = 123;
s[l + l1] = bound++; // 分隔符
id[l + l1] = i;
s[l + l1 + 1] = 0;
}
if(c == 1) {
puts(s1); continue;
}
n = strlen(s) + 1; // 保证 s[n-1] = 0
build_sa(128);
getHeight();
int l = 0, r = 1000, mid, ans = 0;
while(l <= r) {
mid = (l + r) / 2;
if(check(c, mid)) { ans = mid; l = mid + 1; }
else r = mid - 1;
}
if(ans == 0) puts("?");
else print(c, ans);
}
return 0;
}

uva11107(后缀数组)的更多相关文章

  1. uva11107 后缀数组

    题意给了n个串 然后计算 这些串中的子串在大于1/2的串中出现 求出这个串的最长长度. 将这些串用一个每出现的不同的字符拼起来 ,然后二分找lcp #include <iostream> ...

  2. UVA11107 Life Forms --- 后缀数组

    UVA11107 Life Forms 题目描述: 求出出现在一半以上的字符串内的最长字符串. 数据范围: \(\sum len(string) <= 10^{5}\) 非常坑的题目. 思路非常 ...

  3. 【UVA11107 训练指南】Life Forms【后缀数组】

    题意 输入n(n<=100)个字符串,每个字符串长度<=1000,你的任务是找出一个最长的字符串使得超过一半的字符串都包含这个字符串. 分析 训练指南上后缀数组的一道例题,据说很经典(估计 ...

  4. 后缀数组的倍增算法(Prefix Doubling)

    后缀数组的倍增算法(Prefix Doubling) 文本内容除特殊注明外,均在知识共享署名-非商业性使用-相同方式共享 3.0协议下提供,附加条款亦可能应用. 最近在自学习BWT算法(Burrows ...

  5. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  6. BZOJ 1692: [Usaco2007 Dec]队列变换 [后缀数组 贪心]

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1383  Solved: 582[Submit][St ...

  7. POJ3693 Maximum repetition substring [后缀数组 ST表]

    Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9458   Acc ...

  8. POJ1743 Musical Theme [后缀数组]

    Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 27539   Accepted: 9290 De ...

  9. 后缀数组(suffix array)详解

    写在前面 在字符串处理当中,后缀树和后缀数组都是非常有力的工具. 其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料. 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现, ...

随机推荐

  1. python之if测试

    (一)python的条件判断语句一般格式如下: if (条件1): (执行结果) elif(条件2): (执行结果) ..... else: (执行结果) 执行顺序为从上到下判断,若条件1不符合则进入 ...

  2. ironic baremetal node status

    参考: https://docs.openstack.org/ironic/latest/contributor/states.html https://docs.openstack.org/iron ...

  3. ironic-conductor与ipa交互clean部分代码分析

    clean的动作会在provide和delete阶段才会触发 从代码分析: 对节点执行的node provide/deleted/clean会先发送到ironicclient ironicclient ...

  4. centos6系列问题

    一.NetworkManager启动问题 1.由于以后要支持e1000版虚拟化网卡,所有centos6镜像均按照NetworkManager服务,并设置开机自启动 2.虚机启动时,默认是Network ...

  5. OpenStack-Ironic裸金属简介

    一,Ironic简述 简而言之,OpenStack Ironic就是一个进行裸机部署安装的项目.    所谓裸机,就是指没有配置操作系统的计算机.从裸机到应用还需要进行以下操作:  (1)硬盘RAID ...

  6. Android 图片文字单位 px、dp、sp区别

    文章来源:http://www.cnblogs.com/bjzhanghao/archive/2012/11/06/2757300.html px:像素,一个像素点,1px代表屏幕上一个物理的像素点: ...

  7. c++知识点总结-模板特化

    类模板的全特化与偏特化 类模板 template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b ...

  8. nyoj 题目37 回文字符串

    回文字符串 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba".当 ...

  9. Codeforces Round #324 (Div. 2) D

    D. Dima and Lisa time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  10. Multiplication Game(博弈论)

    Description Alice and Bob are in their class doing drills on multiplication and division. They quick ...