POJ 3581:Sequence(后缀数组)
题意
给出n个数字的序列,现在让你分成三段,使得每一段翻转之后拼接起来的序列字典序最小。保证第一个数是序列中最大的数。
例如样例是{10, 1, 2, 3, 4},分成{1, 10}, {2}, {3,4},最后字符串变成{1, 10, 2, 4, 3}。
思路
首先考虑第一段,因为第一个数是最大的,因此只要使得某一个前缀翻转后的字典序最小,所以只要选择序列翻转后最小后缀。
对于第二段和第三段,如果像第一段一样直接找剩下的里面最小的后缀,是不可行的,因为直接取第二段最优就忽略了第三段的最优。
例如对于这个剩下的序列:{3, 1, 2, 3, 1, 4},翻转后:{4, 1, 3, 2, 1, 3},取最小后缀{1, 3}作为第二段,那么最后得到的第二三段翻转后的后缀合起来是:{1, 3, 4, 1, 3, 2}。
这样不如取后缀{1, 3, 2, 1, 3}作为第二段更优,最后得到{1, 3, 2, 1, 3, 4}。
可以发现,这是一个类似于环的东西,即第一段取了{1,3},剩下一段{4, 1, 3, 2}就是接在{1,3}后面的,构成了一个环状的序列。
那么现在问题就转化为,求一个环状序列长度为n的字典序最小序列了。
因此,可以直接复制一遍序列放在后面,变成{4, 1, 3, 2, 1, 3, 4, 1, 3, 2, 1, 3},这样再对这个序列求一个最小后缀(这个最小后缀要合法),得到的就是可行的了。
回过头来,为什么第一段就可以直接求?
因为第一个数是最大的,像个墙一样卡在那里,如果也是复制一遍的话,可以发现其实根本取不到后面(取[n,2*n)的字典序不可能比取[0,n)的后缀更小)。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 11;
int t1[N*2], t2[N*2], c[N*2], s[N*2], str[N*2], Hash[N], sa[N*2], n;
bool cmp(int *r, int a, int b, int l) {
return r[a] == r[b] && r[a+l] == r[b+l];
}
void SA(int s[], int sa[], int n, int m) {
int i, j, p, *x = t1, *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(j = 1; j <= n; j <<= 1) {
p = 0;
for(i = n - j; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 1; 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]] = cmp(y, sa[i-1], sa[i], j) ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}
int main() {
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &s[i]), Hash[i] = s[i];
sort(Hash, Hash + n);
int m = unique(Hash, Hash + n) - Hash;
// 离散化后注意区间是到m不是到n !!!
for(int i = 0; i < n; i++) s[i] = lower_bound(Hash, Hash + m, s[i]) - Hash;
int p1, p2;
reverse_copy(s, s + n, str);
SA(str, sa, n, m);
for(int i = 0; i < n; i++) {
p1 = n - sa[i];
if(p1 >= 1 && n - p1 >= 2) break;
// 第一段至少有1个数并且剩下至少两个数(因为后面要分成两段)
}
int nn = n - p1;
reverse_copy(s + p1, s + n, str);
reverse_copy(s + p1, s + n, str + nn);
SA(str, sa, 2 * nn, m);
for(int i = 0; i < 2 * nn; i++) {
if(sa[i] >= nn) continue;
p2 = nn - sa[i] + p1;
if(p2 - p1 >= 1 && n - p2 >= 1) break;
// 第二段至少有1个数并且至少剩下一个数(还剩下一段)
}
for(int i = p1 - 1; i >= 0; i--) printf("%d\n", Hash[s[i]]);
for(int i = p2 - 1; i >= p1; i--) printf("%d\n", Hash[s[i]]);
for(int i = n - 1; i >= p2; i--) printf("%d\n", Hash[s[i]]);
return 0;
}
/*
8
5 0 3 1 2 3 1 4
0
5
1
3
2
1
3
4
*/
POJ 3581:Sequence(后缀数组)的更多相关文章
- POJ 3581 Sequence(后缀数组)
Description Given a sequence, {A1, A2, ..., An} which is guaranteed A1 > A2, ..., An, you are to ...
- POJ 3581 Sequence ——后缀数组 最小表示法
[题目分析] 一见到题目,就有了一个显而易见obviously的想法.只需要每次找到倒过来最小的那一个字符串翻转就可以了. 然而事情并不是这样的,比如说505023这样一个字符串,如果翻转了成为320 ...
- POJ 3581 Sequence [后缀数组]
Sequence Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 6911 Accepted: 1543 Case Tim ...
- [POJ 3581]Sequence
[POJ 3581]Sequence 标签: 后缀数组 题目链接 题意 给你一串序列\(A_i\),保证对于$ \forall i \in [2,n],都有A_1 >A_i$. 现在需要把这个序 ...
- 后缀数组 POJ 3581 Sequence
题目链接 题意:把n个数字(A1比其他数字都大)的序列分成三段,每段分别反转,问字典序最小的序列. 分析:因为A1比其他数字都大,所以反转后第一段结尾是很大的数,相当是天然的分割线,第一段可以单独考虑 ...
- POJ 3581 Sequence(后缀数组)题解
题意: 已知某字符串\(str\)满足\(str_1 > max\{str_2,str_3 \cdots str_n\}\),现要求把这个字符串分成连续的三组,然后每组都翻转,问字典序最小是什么 ...
- POJ3581 Sequence —— 后缀数组
题目链接:https://vjudge.net/problem/POJ-3581 Sequence Time Limit: 5000MS Memory Limit: 65536K Total Su ...
- POJ 2406 KMP/后缀数组
题目链接:http://poj.org/problem?id=2406 题意:给定一个字符串,求由一个子串循环n次后可得到原串,输出n[即输出字符串的最大循环次数] 思路一:KMP求最小循环机,然后就 ...
- POJ 1743-POJ - 3261~后缀数组关于最长字串问题
POJ 1743 题意: 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1~~88范围内的整数,现在要找一个重复的主题.“主题”是整个音符序列的一个子串,它需 ...
- POJ - 1226 Substrings (后缀数组)
传送门:POJ - 1226 这个题跟POJ - 3294 和POJ - 3450 都是一样的思路,一种题型. POJ - 3294的题解可以见:https://www.cnblogs.com/li ...
随机推荐
- 关于VS2015中的code snippet无法使用的问题
什么是code snippet? Code snippets are small blocks of reusable code that can be inserted in a code file ...
- TCP 三次握手(相当于寄信需要回执,第一次握手:我寄给你一封信。第二次握手:你回我一封信。第三次握手:我再给你一个回执,这样你才能确认我收到信了)
TCP 连接是通过三次握手进行初始化的.三次握手的目的是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息.以下步骤概述了通常情况下客户端计算机联系服务器计算机的过程: 1. 客户端向服务器发送 ...
- STM32 模拟I2C (STM32F051)
/** ****************************************************************************** * @file i2c simu. ...
- sql分组统计多列值
select BQDM,sum(case when HFBZ='0' then 1 ELSE 0 end) bxschf,sum(case when HFBZ='1' then 1 ELSE 0 en ...
- ADB 基础命令使用
1.adb shell(>=2个设备显示:error: more than one device/emulator,仅连接一个设备可用) adb -d shell 只运行在真实设备中 adb - ...
- 零元学Expression Blend 4 - Chapter 8 用实例了解布局容器系列-「Grid」
原文:零元学Expression Blend 4 - Chapter 8 用实例了解布局容器系列-「Grid」 本系列将教大家以实做案例认识Blend 4 的布局容器,此章介绍的是Blend 4 里的 ...
- tf.nn.softmax & tf.nn.reduce_sum & tf.nn.softmax_cross_entropy_with_logits
tf.nn.softmax softmax是神经网络的最后一层将实数空间映射到概率空间的常用方法,公式如下: \[ softmax(x)_i=\frac{exp(x_i)}{\sum_jexp(x_j ...
- C# 对字段忽略模型校验
1.在if (!ModelState.IsValid)之前给字段赋值,然后TryUpdateModel()2.使用如下方法 public ActionResult Create([Bind(Exclu ...
- 《C++ Primer》读书笔记 第三章
1.注意:头文件不应包含using声明.因为头文件的内容会拷贝到所有引用他的文件中去,对于某些程序来说,由于不经意间包含了一些名字,可能会产生名字冲突.2.string类型的读入:用cin读入stri ...
- Dev C++ 工程没有调试信息 解决办法
Dev C++ 工程没有调试信息 解决方法DevC++4.9.9.2中,按 F8 开始调试.提示信息为:工程没有调试信息,您想打开工程的调试选项并重新生成吗?选择是后,再按F8,仍旧是这个信息.什么原 ...