## 问题描述
给定长度为N的字符串S,要构造一个长度为N字符串T。T是一个空串,反复执行下列任意操作:
* 从S的头部删除一个字符,加到T的尾部;
* 从S的尾部删除一个字符,加到T的尾部;
目标是要构造字典序尽可能小的字符串T。
**限制条件:**
* 1<=N<=2000
* 字符串S只包含大写英文字母
**输入:**
N = 6
S = "ACDBCB"
**输出:**
ABCBCD
![](https://box.kancloud.cn/3ff642d2f5223efaa650f881ce74cd10_426x186.png)
## 问题分析
从字典序的性质上看,无论T的末尾有多大,只要前面部分的较小就可以。所以我们可以试一下如下贪心算法:
* 不断取S的开头和末尾中较小的一个字符放到T的末尾。 这个算法已经接近正确了,只是针对S的开头和末尾字符相同的情形还没有定义。在这种情形下,因为我们希望能够尽早使用更小的字符,所以就要比较下一个字符的大小。下一个字符也有可能相同,因此就有如下算法:
* 按照字典序比较S和将S反转后的字符串S'。
* 如果S较小,就从S的开头取出一个文字,追加到T的末尾。
* 如果S'较小,就从S的末尾取出一个文字,追加到T的末尾。 ( 如果相同则取哪个都可以)
## Code
~~~
int n;
char ch[maxn];
void solve()
{
// 剩余的字符串为ch[a],ch[a+1],...,ch[b]
int a=0,b=n-1;
int k=0;
while(a<=b)
{
// 将从左起和从右起的字符串比较
int flag=0;
for(int i=0;i+a<=b;i++)
{
if(ch[a+i]>ch[b-i])
{
flag=0;
break;
}
else if(ch[b-i]>ch[a+i])
{
flag=1;
break;
}
}
if(!flag)
printf("%c",ch[b--]);
else
printf("%c",ch[a++]);
k++;
}
printf("\n");
}
~~~
对应题目:[POJ3617](http://poj.org/problem?id=3617)
- 简介
- 零基础快速入门ACM
- C语言快速入门
- C语言快速入门
- C/C++基础
- 输入输出
- 数组和字符串
- 数组
- 字符数组
- 函数和递归
- C++标准容器库(STL)
- Vector
- Map
- Set
- String
- Stack
- Queue
- Priority_queue
- 贪心
- 硬币问题
- 区间调度问题
- 字典序最小问题
- 独木舟问题
- 搜索
- DFS
- BFS
- 剪枝
- 记忆化搜索
- 常用技巧
- 倍增
- 高精度
- 大数加法
- 大数减法
- 大数乘法
- 大数除法
- 大数阶乘
- 输入挂
- 前缀和
- 二分
- 本地预处理
- 尺取
- 枚举
- 坐标离散化
- 分治
- 数列分治
- 树上分治
- 平面分治
- 计算几何
- 凸包
- 点积
- 叉积
- 线段关系
- 皮克定理
- 最近点对
- 数据结构
- 线段树
- 树状数组
- 并查集
- 动态规划
- 基础知识
- 信息学竞赛中的动态规划
- 动态规划初步
- 最长上升子序列(LIS)
- 最长公共子序列(LCS)
- 最大子段和
- 背包问题
- 部分背包
- 0 1 背包
- 完全背包
- 多重背包
- 背包的可行性问题
- 线性DP
- 树形DP
- 区间DP
- 数位DP
- 动态规划优化
- 字符串
- KMP算法
- 拓展KMP
- 字符串Hash
- Manacher算法
- 后缀数组
- Trie树
- 博弈论
- Nim博弈
- Bash博弈
- 斐波那契博弈
- 威佐夫博弈
- SG函数
- 数论
- 整数研究
- 素数判断
- 素数筛选
- 快速幂
- 算数基本定理
- 最大公约数(Gcd)
- 费马小定理
- 扩展欧几里得
- 逆元
- 同余定理
- 组合数学
- 容斥原理
- 抽屉原理
- 卢卡斯(Lucas)
- 卡特兰(Catalan)
- 著名函数
- 欧拉函数
- 莫比乌斯函数
- 线性代数
- 矩阵运算
- 矩阵快速幂
- 图论
- 存图方法
- 邻接矩阵
- 邻接表
- Vector存图
- 链式前向星
- 图的遍历
- 拓扑排序
- 最短路
- Dijkstra算法
- SPFA算法
- Floyed 算法
- 最小生成树
- Prim算法
- Kruskal算法
- 最近公共祖先 (LCA)
- 二分图匹配
- 网络流
- 其他
- 莫队