POJ 3581 Sequence
2012-03-10 19:37
302 查看
题目大意:
给N个数(N不超过200000), 保证第一个数比后面的都大, 要求你把数列分成连续的三段, 分别逆序再拼起来, 要求新的串字典序最小.
简要分析:
一个直接的思路是想把最小的一个数作为第一段的末尾, 这样是不是最优的呢? 是的, 因为第一个数比后面的数都大, 于是你把所有的最小的数作为第一段的末尾逆序时, 最大的数之前的部分的字典序肯定不一样. 于是就先把整个序列逆序, 求一个后缀数组, 按i从小到大考虑sa[i], 因为第一个串的长度最多是N - 2, 所以sa[i]必须大于2, 找到最小的i即可.
于是问题转化为, 一个数字串, 分成两段并分别逆序, 使得字典序最小. 假设序列为s, 长度为m, 第一串的末尾为k, 则原来的串s1s2..sk..sk+1sk+2..sm, 在操作后变成了sksk-1..s1smsm-1..sk+1, 注意到这个串是smsm-1sm-2..s1smsm-1sm-2..s1的子串, 于是做法就明了了: 把s逆序并在后面复制一遍, 做一次后缀数组, 找到最小的i, 使得i不超过m, 且要求它大于1(要保证是两个串). 于是时间复杂度O(NlogN), 其实就是后缀数组的复杂度, 我写的倍增啦~
代码实现:
View Code
给N个数(N不超过200000), 保证第一个数比后面的都大, 要求你把数列分成连续的三段, 分别逆序再拼起来, 要求新的串字典序最小.
简要分析:
一个直接的思路是想把最小的一个数作为第一段的末尾, 这样是不是最优的呢? 是的, 因为第一个数比后面的数都大, 于是你把所有的最小的数作为第一段的末尾逆序时, 最大的数之前的部分的字典序肯定不一样. 于是就先把整个序列逆序, 求一个后缀数组, 按i从小到大考虑sa[i], 因为第一个串的长度最多是N - 2, 所以sa[i]必须大于2, 找到最小的i即可.
于是问题转化为, 一个数字串, 分成两段并分别逆序, 使得字典序最小. 假设序列为s, 长度为m, 第一串的末尾为k, 则原来的串s1s2..sk..sk+1sk+2..sm, 在操作后变成了sksk-1..s1smsm-1..sk+1, 注意到这个串是smsm-1sm-2..s1smsm-1sm-2..s1的子串, 于是做法就明了了: 把s逆序并在后面复制一遍, 做一次后缀数组, 找到最小的i, 使得i不超过m, 且要求它大于1(要保证是两个串). 于是时间复杂度O(NlogN), 其实就是后缀数组的复杂度, 我写的倍增啦~
代码实现:
View Code
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 200000, MAX_R = MAX_N * 2; int n, s[MAX_R + 1], sa[MAX_R + 1], rank[MAX_R + 1], height[MAX_R + 1], cnt[MAX_R + 1]; int t[MAX_R + 1], o[MAX_N + 1], m; struct node_t { int v[2], p; bool operator == (const node_t &t) const { return v[0] == t.v[0] && v[1] == t.v[1]; } } nd[MAX_R + 1], tp[MAX_R + 1]; int find(int x) { int l = 1, r = m + 1; while (l + 1 < r) { int mid = (l + r) >> 1; if (o[mid] <= x) l = mid; else r = mid; } return l; } void ra(int b, int l) { for (int i = 1; i >= 0; i --) { memset(cnt, 0, sizeof(int) * (b + 1)); for (int j = 1; j <= l; j ++) cnt[nd[j].v[i]] ++; for (int j = 1; j <= b; j ++) cnt[j] += cnt[j - 1]; for (int j = l; j >= 1; j --) tp[cnt[nd[j].v[i]] --] = nd[j]; memcpy(nd, tp, sizeof(node_t) * (l + 1)); } for (int i = 1, j = 1, k = 1; i <= l; i = j, k ++) while (j <= l && nd[j] == nd[i]) rank[nd[j ++].p] = k; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i ++) scanf("%d", &s[i]); memcpy(o + 1, s + 1, sizeof(int) * n); sort(o + 1, o + n + 1); m = unique(o + 1, o + n + 1) - (o + 1); for (int i = 1; i <= n; i ++) s[i] = find(s[i]); memcpy(t + 1, s + 1, sizeof(int) * n); reverse(t + 1, t + n + 1); for (int i = 1; i <= n; i ++) nd[i].v[0] = t[i], nd[i].v[1] = 0, nd[i].p = i; ra(m, n); for (int st = 1; st < n; st <<= 1) { for (int i = 1; i <= n; i ++) { nd[i].v[0] = rank[i], nd[i].v[1] = i + st <= n ? rank[i + st] : 0; nd[i].p = i; } ra(n, n); } for (int i = 1; i <= n; i ++) sa[rank[i]] = i; int fir = 1; while (sa[fir] <= 2) fir ++; for (int i = sa[fir]; i <= n; i ++) printf("%d\n", o[t[i]]); memset(t, 0, sizeof(t)); int new_n = sa[fir] - 1; for (int i = 1; i <= new_n; i ++) t[i] = s[i + n - sa[fir] + 1]; reverse(t + 1, t + new_n + 1); for (int i = 1; i <= new_n; i ++) t[i + new_n] = t[i]; new_n *= 2; for (int i = 1; i <= new_n; i ++) nd[i].v[0] = t[i], nd[i].v[1] = 0, nd[i].p = i; ra(m, new_n); for (int st = 1; st < new_n; st <<= 1) { for (int i = 1; i <= new_n; i ++) { nd[i].v[0] = rank[i], nd[i].v[1] = i + st <= new_n ? rank[i + st] : 0; nd[i].p = i; } ra(new_n, new_n); } for (int i = 1; i <= new_n; i ++) sa[rank[i]] = i; fir = 1; while (sa[fir] > new_n / 2 || sa[fir] == 1) fir ++; for (int i = 0; i < new_n / 2; i ++) printf("%d\n", o[t[sa[fir] + i]]); return 0; }
相关文章推荐
- POJ 3581 Sequence(后缀数组)
- POJ 3581 Sequence(后缀数组)
- (后缀数组)poj 3581 Sequence
- POJ3581---Sequence(后缀数组)
- POJ 3581 Sequence(后缀数组)
- Poj 3581 Sequence (离散化+后缀数组)
- POJ 3581 Sequence 笔记
- 后缀数组 POJ 3581 Sequence
- POJ 3581 Sequence
- POJ 3581 Sequence(后缀数组)
- POJ 3581 Sequence [后缀数组]
- poj 3581 Sequence(后缀数组)
- POJ - 3581 Sequence
- POJ 3581-Sequence(后缀数组)
- POJ 题目3581 Sequence(后缀数组+离散化)
- POJ 3581 Sequence ——后缀数组 最小表示法
- POJ 3581 Sequence(后缀数组)
- POJ 3581 Sequence
- |Poj 3581|后缀数组|Sequence
- POJ 3581 Sequence(后缀数组)