题目

http://poj.org/problem?id=3080

题意

有m个(2<=m<=10)不包含空格的字符串,长度为60个字符,求所有字符串中都出现过的最长公共子序列,若该子序列长度小于3,输出"no significant commonalities",否则,输出字典序最小且长度最大的公共子序列。

思路

由于数据较小,其实可以使用比较暴力的思路,但这里为了复习后缀数组采用后缀数组的方法。

1. 将多个字符串合为一个目标字符串buff,为了防止程序无法分清字符串连接处,每个连接处添加一个空格,形如AAAA GGGG。

2. 建立后缀数组,这里采用基数排序的方法。

a. 令rk数组记录后缀的字典序排名,也即rk[i]为从第i个字符开始的后缀的字典序排名,排名从0开始。

b. 令sa数组记录排序后的后缀,也即sa[i]为字典序第i的后缀的第一个字符的位置,例如buff='abba',则sa[0]=4代表空串,sa[1]=3代表a,sa[2]=0代表abba,依次类推。

c. 进行基数排序的初始排序,排序第一个字符。

d. 初始化跨度k为1,每次完成下面的循环,k乘以2倍,也即每次通过目前的rk数组把已经排序的部分长度扩展2倍。

e. 用基数排序对后缀[k,2k]进行排序。

解释:此时后缀[0,k]有序,也即前k个字符一定是按照字母序排序的,举例来说,排序可能是这样,k=2:空串,a,aadd,aacc,abaa,abcc。

此时后面k个字符,也即[k,2k]部分是无序的,但是[k,2k]部分的顺序可以和[0,k]部分一一对应起来,因为[k,2k]部分如果不是空串,那么一定是另一个后缀的[0,k]部分。

这一步根据这种关系将后缀数组sa排列为[k,2k]为主键,[0,k]为第二位键字典序递增顺序。

f. 用基数排序对后缀[0,k]进行排序。

解释:由于上一步排列的顺序,根据基数排序的特点,后缀数组将会变为[0.k]为主键,[k,2k]为第二位键字典序递增顺序,也即[0,2k]遵从字典序排列。

g. 根据sa计算rk,此处若两个后缀[0,2k]部分相同,则对应rk也应当相同。

h. 循环直至k >= len,此时明显全部遵从字典序排列。

i. 根据sa计算rk,此处rk[sa[i]] = i,此时rk的含义将发生改变,变为sa的索引,注意此处若两个后缀[0,2k]相同,其对应rk不相同。

3. 建立高度数组h。(对于从i开始的后缀,如果其高度(即该后缀和在字典序上最接近它且比它字典序小的那个后缀的公共前缀长度)为height,那么i+1开始的后缀高度至少为height-1,利用这一性质,以字符串原有的顺序遍历后缀并且计算其高度。)

4. 得出答案,由题意,设答案的长度为ans,第一个字符是目标串的第start个字符,则h数组中一定有一段连续的子序列都大于等于ans,且对应的后缀分别来自目标字符串的m段。

a. 建立优先队列,用于存储后缀的高度和index,并获知在当前扫描的这一段中出现过的最小高度。

b. 用cnt数组监视当前扫描的这一段中每段有多少个后缀出现。可以通过cnt数组获知被扫描的这一段中目标段中有多少段的后缀出现过,设这个值为num。

c. 设起点为s,若目标串长度为len,则s从0循环至len,对每次循环:

i. 初始化e=s+1,e不断向后扫,h[s,e]即为当前被扫描的段,直到num==m。

ii.将最小高度与目前存储的ans进行比较,若最小高度较大,则ans赋值为最小高度,start赋值为s。

解释:由于rk和h是对应的,所以start被赋予的会是字典序最小长度最长的公共子序列的rk,可以通过sa输出答案。

感想

这道题卡在三个点上:1. 基数排序,先对后半段排序,再对前半段排序。

2. 基数排序,因为在排序过程中将len加入排序,导致边界出错。

3. 基数排序,因为rk是对应于目标串的位置的,所以先对后半段排序时不用改变rk。

代码

 #include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <iostream>
#include <assert.h>
#include <sstream>
#include <cctype>
#include <queue>
#include <stack>
#include <map>
#include <iterator>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ld; const int basenum = ;
const int basenump1 = basenum + ;
const int maxm = ;
const int maxn = basenump1 * maxm + basenump1; char buff[maxn]; int m;
int len; int rk[maxn];
int trk[maxn];
int sa[maxn];
int tsa[maxn];
int cnt[maxn];
int h[maxn]; void debugrk(int *sa, int l){
for(int i = ;i <= len;i++){
printf("rk[%d]:%d ",i,rk[i]);
for(int j = i;j < len && j < i + l;j++){
putchar(buff[j]);
}
puts("");
}
puts("");
} void debugsa(int *sa, int l){
for(int i = ;i <= len;i++){
printf("sa[%d]:%d rk %d ",i,sa[i], rk[sa[i]]);
for(int j = sa[i];j < len && j < sa[i] + l;j++){
putchar(buff[j]);
}
puts("");
}
puts("");
} void debugh(){
for(int i = ;i <= len;i++){
printf("POS: %d h[%d]:%d ", sa[i],i,h[i]);
for(int j = sa[i];j < len;j++){
putchar(buff[j]);
}
puts("");
}
puts("");
} void input()
{
scanf("%d",&m);
len = m * basenump1 - ;
for(int i = ; i < m; i++)
{
scanf("%s", buff + i * basenump1);
if(i + < m)buff[i * basenump1 + basenum] = ' ';
}
} void build()
{
memset(rk, , sizeof rk);
memset(cnt, , sizeof cnt);
rk[len] = trk[len] = ;
sa[] = tsa[] = len;
for(int i = ; i < len; i++)
{
rk[i] = buff[i];
cnt[rk[i]]++;
}
// first radix sort
for(int i = ; i < ; i++)
{
cnt[i] += cnt[i - ];
}
for(int i = len - ; i >= ; i--)
{
sa[cnt[rk[i]]--] = i;
}
for(int k = ; k < len; k <<= )
{
memset(cnt, , sizeof cnt);
for(int i = ; i < len; i++)
{
cnt[(i + k < len)?rk[i + k]:]++;
}
/*
Or you can use
for(int i = len;i > 0;i--){
cnt[(sa[i] + k < len)?rk[sa[i] + k]:0]++;
}
*/
for(int i = ; i <= len; i++)
{
cnt[i] += cnt[i - ];
}
for(int i = len; i > ; i--)
{
int ind = cnt[(sa[i] + k < len)?rk[sa[i] + k]:]--;
tsa[ind] = sa[i];
}
memset(cnt, , sizeof cnt);
for(int i = ; i < len; i++)
{
cnt[rk[i]]++;
}
/*
Or you can use
for(int i = len;i > 0;i--){
cnt[rk[sa[i]]]++;
}
*/
for(int i = ; i <= len; i++)
{
cnt[i] += cnt[i - ];
}
for(int i = len; i > ; i--)
{
int ind = cnt[rk[tsa[i]]]--;
sa[ind] = tsa[i];
}
for(int i = , now = sa[i], former = sa[i - ]; i <= len; i++, now = sa[i], former = sa[i - ])
{
trk[now] = trk[former];
int nownxt = min(now + k, len);
int formernxt = min(former + k, len);
if(rk[now] != rk[former] || rk[nownxt] != rk[formernxt]){ trk[now]++;
}
}
for(int i = ; i < len; i++)
{
rk[i] = trk[i];
}
}
for(int i = ;i <= len;i++){
rk[sa[i]] = i;
}
memset(h, , sizeof h);
//debugsa(sa, len);
//debugrk(sa, len);
for(int i = , height = ; i < len; i++)
{
if(height)height--;
int former = sa[rk[i] - ];
while(i + height < len && former + height < len && buff[i + height] == buff[former + height])height++;
h[rk[i]] = height;
}
//debugh();
} void solve()
{
build();
memset(cnt, , sizeof cnt);
priority_queue<P, vector<P>, greater<P> >que;
int num = ;
int ans = ;
int start = ;
for(int s = ,e = ; s <= len; s++)
{
while(num < m && e <= len)
{
que.push(P(h[e], e));
int ind = sa[e] / basenump1;
if(cnt[ind] == )num++;
cnt[ind]++;
e++;
}
while(que.top().second < s){
que.pop();
}
if(num == m && ans < que.top().first)
{
ans = que.top().first;
start = s - ;
}
if(s > ){
int ind = sa[s - ] / basenump1;
if(cnt[ind] == )num--;
cnt[ind]--;
}
}
if(ans < )puts("no significant commonalities");
else
{
for(int i = ; i < ans; i++)
{
putchar(buff[sa[start] + i]);
}
putchar('\n');
}
} int main()
{
freopen("input.txt", "r", stdin);
int t;
scanf("%d", &t);
for(int ti = ; ti < t; ti++)
{
input();
solve();
}
return ;
}

POJ 3080 Blue Jeans 后缀数组, 高度数组 难度:1的更多相关文章

  1. POJ 3080 Blue Jeans (后缀数组)

    题目大意: 求出这些DNA序列中的最长且字典序最小的公共子串. 思路分析: 二分长度的答案,去height中扫描这个长度是否满足,一旦满足就立即输出.这样就能够保证字典序最小了. #include & ...

  2. POJ 3080 Blue Jeans (求最长公共字符串)

    POJ 3080 Blue Jeans (求最长公共字符串) Description The Genographic Project is a research partnership between ...

  3. poj 3080 Blue Jeans

    点击打开链接 Blue Jeans Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10243   Accepted: 434 ...

  4. POJ 3080 Blue Jeans (字符串处理暴力枚举)

    Blue Jeans  Time Limit: 1000MS        Memory Limit: 65536K Total Submissions: 21078        Accepted: ...

  5. POJ 3080 Blue Jeans(Java暴力)

    Blue Jeans [题目链接]Blue Jeans [题目类型]Java暴力 &题意: 就是求k个长度为60的字符串的最长连续公共子串,2<=k<=10 规定: 1. 最长公共 ...

  6. POJ 3080 Blue Jeans 找最长公共子串(暴力模拟+KMP匹配)

    Blue Jeans Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20966   Accepted: 9279 Descr ...

  7. POJ 3080 Blue Jeans(后缀数组+二分答案)

    [题目链接] http://poj.org/problem?id=3080 [题目大意] 求k个串的最长公共子串,如果存在多个则输出字典序最小,如果长度小于3则判断查找失败. [题解] 将所有字符串通 ...

  8. POJ 3080 Blue Jeans、POJ 3461 Oulipo——KMP应用

    题目:POJ3080 http://poj.org/problem?id=3080 题意:对于输入的文本串,输出最长的公共子串,如果长度相同,输出字典序最小的. 这题数据量很小,用暴力也是16ms,用 ...

  9. poj 3080 Blue Jeans 解题报告

    题目链接:http://poj.org/problem?id=3080 该题属于字符串处理中的串模式匹配问题.题目要求我们:给出一个DNA碱基序列,输出最长的相同的碱基子序列.(保证在所有的序列中都有 ...

随机推荐

  1. css 可继承属性 display:inline-block 历史

    1. css 可继承属性 1.1 font font-family:规定元素的字体系列font-weight:设置字体的粗细font-size:设置字体的尺寸font-style:定义字体的风格fon ...

  2. 简单实现Java的RMI——远程方法调用

    一.RMI简介: 说到RMI就不得不说RPC了. RPC:(Remote Procedure Call),远程过程调用. RMI(Remote Method Invocation),远程方法调用. R ...

  3. 02-python-垃圾回收机制

    转载自 https://www.cnblogs.com/Xjng/p/5128269.html 加以整理,方便记忆 1垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺 ...

  4. Unity --- 纹理为什么要设置为2的N次方

    1.图片的纹理像素在Unity3D中需要遵循2的N次方,由图形学决定的,只识别2的N次方.   非2的N次方的图片会转化为2的N次方图片(500 x 500 → 512 x 512),是因为转化过程比 ...

  5. 响应式图片 (responsive image)

    更新 : 2019-02-21 除了写 srcset sizes 还有一种 x1, x2, x3, x4 的写法. 我们对比一下 假设 pc 希望是 1000w mobile 希望是 300w siz ...

  6. 图片方向 image orientation Exif

    更新 : 2019-01-02 refer https://stackoverflow.com/questions/3129099/how-to-flip-images-horizontally-wi ...

  7. 图像识别 | AI在医学上的应用 | 深度学习 | 迁移学习

    参考:登上<Cell>封面的AI医疗影像诊断系统:机器之心专访UCSD张康教授 Identifying Medical Diagnoses and Treatable Diseases b ...

  8. apply、call

    call(),apply() 1.每个函数都包含两个非继承而来的方法:call()和apply() 2.在特定的作用域内调用函数,等于设置函数体内的this对象,以扩充函数赖以运行的作用域 3.app ...

  9. caffe-ssd

    1.安装依赖 1 sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-ser ...

  10. Django 的 orm 查询

    一.模型关系表 1. 一对一 Author-AuthorDetail 关联字段可以在任意表下,但必须唯一约束.(unique约束) ad_id(unique约束) ad = models.oneToO ...