题目链接

题意

给出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 40
5
1
3
2
1
3
4
*/

转载于:https://www.cnblogs.com/fightfordream/p/7772164.html

POJ 3581:Sequence(后缀数组)相关推荐

  1. POJ - 3581 Sequence(后缀数组)

    题目链接:点击查看 题目大意:给出一个数列,保证第一个数一定大于其余的所有数,现在需要让我们将其分为连续的三段,每一段独立反转后保证字典序最小,题目要求输出反转后字典序最小的数列 题目分析:因为第一个 ...

  2. POJ 3581 Sequence ——后缀数组 最小表示法

    [题目分析] 一见到题目,就有了一个显而易见obviously的想法.只需要每次找到倒过来最小的那一个字符串翻转就可以了. 然而事情并不是这样的,比如说505023这样一个字符串,如果翻转了成为320 ...

  3. POJ 3415 (后缀数组)

    被虐残了T_T.开始没思路,膜拜大牛的思路又看不懂...推荐一个题解:http://hi.baidu.com/fpkelejggfbfimd/item/5c76cfcba28fba26e90f2ea6 ...

  4. POJ 2217 Secretary (后缀数组)

    原题链接 Problem Description The basic condition of success of a political party, it is the good Electio ...

  5. POJ 2217 Secretary (后缀数组,挑战程序)

    题目意思: 给出两个字符串,要求两个字符串最长的公共子串(是子串不是子序列) 挑战程序设计竞赛, 383 页 本题要点: 1.高度数组,lcp[i] 表示后缀数组中,第i个字符串 S[sa[i] -] ...

  6. POJ - 1226 Substrings(后缀数组+二分)

    题目链接:点击查看 题目大意:给出n个字符串,求出n个字符串中或者在他们翻转过来的字符串中,出现的最长公共子串的长度 题目分析:说白了就是求n个字符串中的最长公共子串的变形,而这个变形也相当简单,只是 ...

  7. Musical Theme POJ - 1743(后缀数组+二分)

    求不可重叠最长重复子串 对于height[i]定义为sa[i]和 sa[i-1]的最长公共前缀 这个最长公共前缀的值肯定是最大的 证明: 设rank[j] < rank[k], 则不难证明后缀j ...

  8. poj 3261 后缀数组 找反复出现k次的子串(子串能够重叠)

    题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 可是大部分时间是在调试代码.由于模板的全局变量用混了,而自 ...

  9. poj 3261 Milk Patterns 后缀数组 最长重复子串

    http://poj.org/problem?id=3261 给一串数组,数组最少含有k个相同子串,可重叠,求这样子串的最长长度. 后缀数组求出 height[],若连续k个height[]都大于mi ...

  10. POJ 1743 (后缀数组+不重叠最长重复子串)

    题目链接: http://poj.org/problem?id=1743 题目大意:楼教主の男人八题orz.一篇钢琴谱,每个旋律的值都在1~88以内.琴谱的某段会变调,也就是说某段的数可以加减一个旋律 ...

最新文章

  1. Fragment使用LocalBroadcastManager接收广播消息
  2. Barracuda VS antelope
  3. mysql 5.6的gtid_mode_[MySQL 5.6] GTID实现、运维变化及存在的bug
  4. matlab求两向量夹角_Matlab-自动化控制系统设计4频域分析
  5. 在Silverlight3绘制sprite图片的一部分
  6. PicGo+码云(gitee)图床环境搭建
  7. Axure总结和感想
  8. L2-007 家庭房产 (25 point(s))
  9. PHP统计订单表,订单售后表金额
  10. chromium thirt_party skia编译shared_liberary
  11. 分享几个快乐有趣的网站
  12. 标题一定要长~~~~长~~~~~~~~~~~~~~长~~~~~~~~
  13. RGB565和RGB888的转换
  14. 性价比高的/便宜又好用的SSL证书品牌有哪些?
  15. 【微信公众号开发】微信支付-退款通知
  16. iphone阅读html,iphone下Safari查看网页源代码
  17. 笔记:[windows] 简单写一个cmd命令行可执行的脚本.bat
  18. vue 记住密码下次自动登录
  19. 越狱插件找不到依赖包_iphone如何卸载插件依赖包 iphone卸载插件依赖包方法
  20. 假设检验:p-value,FDR,q-value

热门文章

  1. flume 整合kafka
  2. ElasticSearch - term 和 match 的差别
  3. 30页PPT解析微服务架构与最佳实践
  4. 实际生产中,微服务用 Spring Cloud 多还是 Dubbo 多?
  5. 这款中间件支持多线程,居然吊打牛B的 Redis!
  6. Memcached中Libevent和线程池使用初探
  7. 未来互联网最稀缺的能力是什么?
  8. 悲剧啊,疑似食物中毒和空气污染
  9. Python 2 和 3 的区别记录
  10. 雷林鹏分享:C# 事件(Event)