题目翻译:
给定一个长度为 n 的字符串 a ,有一个空串
T ,现在你需要选择 n 次:
将 a 的头部字符删除,加到
T 的尾部。
将 a 的尾部字符删除,加到
T 的尾部。
目标得到字典序最小的
T 。
数据限制:n ≤ 2000。
输出要求:每80个一行。
样例输入输出:
输入: 输出:
6 ABCDCB
A
C
D
B
C
B
思路点拨:
这道题是一道经典的贪心问题,得先弄清楚贪心策略:
要让一个串字典序尽量小,优先让它的第一个字母尽量小,然后再考虑让它的第二个字母尽量
小,再考虑第三个......
于是我们得到了一个自然的贪心算法——每次将 a 的头尾中较小的字符放到
T 的末尾。
例如:a = abdc
我们先比较 a 的头尾,发现 ‘ a ’ 比较小,就把它放到
T 的末尾
重复此步骤,我们就会得到最小的
T :abcd
但是上述算法有一个缺陷:当 a 的头尾字母相同时,没有定义该选择哪一个。
这时我们就得进行判断:
当 a 的头尾字母相同时,考虑从头尾取的第二个字符,优先选择较小的。
当 a 的头尾前两个字符都相同时,考虑从头尾取的第三个字符,优先选择较小的。
......
例如:a = cbac
我们发现 a 的头和为都相同,那我们就得比较他们前面的一位:
发现 ‘ a ’ 比较小,因此我们选尾巴上的 ‘ c ’ ,把它放到
T 的末尾
重复此步骤,我们就会得到最小的
T :cabc
这样,我们就得到了一个正确的贪心算法,代码也迎刃而解了。
参考代码:
#include<iostream>
using namespace std;
int main()
{
int n;
char a[2001];
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];//输入
char ans[2001];//定义一个答案数组
int l=1,r=n,num=0;//定义l和r用来记录现在要比较哪两个位置
//num表示目前ans到第几位了
while(num<=n)//一旦num=n,说明全部取完
{
//如果头小于尾,去头
if(a[l]<a[r]) ans[++num]=a[l++];
//如果两者相等,比较前面
else if(a[l]==a[r])
{
int posl=l+1,posr=r-1;//定义两个指针
while(a[posl]==a[posr]&&posl<posr)
posl++,posr--;//判断到什么时候两者不一样了
//可以进行比较了
ans[++num]=a[posl]<a[posr]?a[l++]:a[r--];//三目运算,取值
}
//如果头大于尾,去尾
else ans[++num]=a[r--];
}
for(int i=1;i<=n;i++)
{
cout<<ans[i];
if(i%80==0) cout<<endl;//格式要求
}
return 0;
}
有什么看不懂的,可以私信我。
本人还是一名小学生,希望多多关照!