POJ 3080 Blue Jeans 后缀数组, 高度数组 难度:1
题目
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的更多相关文章
- POJ 3080 Blue Jeans (后缀数组)
题目大意: 求出这些DNA序列中的最长且字典序最小的公共子串. 思路分析: 二分长度的答案,去height中扫描这个长度是否满足,一旦满足就立即输出.这样就能够保证字典序最小了. #include & ...
- POJ 3080 Blue Jeans (求最长公共字符串)
POJ 3080 Blue Jeans (求最长公共字符串) Description The Genographic Project is a research partnership between ...
- poj 3080 Blue Jeans
点击打开链接 Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 10243 Accepted: 434 ...
- POJ 3080 Blue Jeans (字符串处理暴力枚举)
Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 21078 Accepted: ...
- POJ 3080 Blue Jeans(Java暴力)
Blue Jeans [题目链接]Blue Jeans [题目类型]Java暴力 &题意: 就是求k个长度为60的字符串的最长连续公共子串,2<=k<=10 规定: 1. 最长公共 ...
- POJ 3080 Blue Jeans 找最长公共子串(暴力模拟+KMP匹配)
Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 20966 Accepted: 9279 Descr ...
- POJ 3080 Blue Jeans(后缀数组+二分答案)
[题目链接] http://poj.org/problem?id=3080 [题目大意] 求k个串的最长公共子串,如果存在多个则输出字典序最小,如果长度小于3则判断查找失败. [题解] 将所有字符串通 ...
- POJ 3080 Blue Jeans、POJ 3461 Oulipo——KMP应用
题目:POJ3080 http://poj.org/problem?id=3080 题意:对于输入的文本串,输出最长的公共子串,如果长度相同,输出字典序最小的. 这题数据量很小,用暴力也是16ms,用 ...
- poj 3080 Blue Jeans 解题报告
题目链接:http://poj.org/problem?id=3080 该题属于字符串处理中的串模式匹配问题.题目要求我们:给出一个DNA碱基序列,输出最长的相同的碱基子序列.(保证在所有的序列中都有 ...
随机推荐
- d3.select(this)不能用箭头函数
d3中典型的数据绑定片段 const items = svg.selectAll('g') .data(gdfs,(d)=> d.name); const enter = items.enter ...
- ubuntu 中文设置
1,安装中文语言包 sudo apt-get install language-pack-zh-hans sudo update-locale LANG=zh_CN.UTF-8 添加中文支持: sud ...
- Fiddler 手机抓包介绍
直接打开tools -> Options 进行设置 点击OK,在这里代理就设置完成,一定要重启软件配置才生效,下面是手机端的设置. 手机端代理设置以三星S4为例子,1.如下图真机三星S4设置:找 ...
- Python 编程快速上手 第九章 组织文件
上一章节,主要讲了如何用 Python 进行创建并写入新文件.这一章节,讲了对如何用 Python 对文件进行进一步的操作,包括: 移动,复制,删除文件 改名 压缩文件 [shutil]移动,复制,删 ...
- 常见的ORACLE语句
基本 --新建表: create table table1( id varchar(300) primary key, name varchar(200) not null); --插入数据 inse ...
- MyEclipse下自定义(支持html5的)JSP模板--JSP
需求:由于某些Mclipse版本发行的比较早,所以在有些版本(比如Mclipse2014,至于其他版本博主不知能不能创建html5格式的JSP页面)里创建JSP页面时html部分不是html5格式的. ...
- 老老实实学WCF
老老实实学WCF 第三篇 在IIS中寄宿服务 通过前两篇的学习,我们了解了如何搭建一个最简单的WCF通信模型,包括定义和实现服务协定.配置服务.寄宿服务.通过添加服务引用的方式配置客户端并访问服务.我 ...
- LeetCode--283--移动0
问题描述: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原 ...
- px em rem 区别
PX:PX实际上就是像素,用PX设置字体大小时,比较稳定和精确.但是这种方法存在一个问题,当用户在浏览器中浏览我们制作的Web页面时,如果改变了浏览器的缩放,这时会使用我们的Web页面布局被打破.这样 ...
- 二叉搜索树BST(Binary Search Tree)
二叉搜索树(Binary Search Tree)也叫二叉排序树或二叉查找树.它满足以下性质: 1.非空左子树的所有键值小于其根结点的键值: 2.非空右子树的所有键值大于其根结点的键值: 3.左右子树 ...