携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
题目描述
给定一个字符串 s 和一个整数 k 。你可以从 s 的前 k 个字母中选择一个,并把它加到字符串的末尾。
返回 在应用上述步骤的任意数量的移动后,字典上最小的字符串 。
示例 1:
输入:s = "cba", k = 1
输出:"acb"
解释:
在第一步中,我们将第一个字符(“c”)移动到最后,获得字符串 “bac”。
在第二步中,我们将第一个字符(“b”)移动到最后,获得最终结果 “acb”。
示例 2:
输入:s = "baaca", k = 3
输出:"aaabc"
解释:
在第一步中,我们将第一个字符(“b”)移动到最后,获得字符串 “aacab”。
在第二步中,我们将第三个字符(“c”)移动到最后,获得最终结果 “aaabc”。
提示:
- 1 <= k <= S.length <= 1000
- s 只由小写字母组成。
思路
想了好一会儿,确实没想出来。看了官方题解,感觉有些跳跃,自己记录一下理解。
分成2种情况考虑:
k == 1k > 1
k == 1
每次只能移动对首字符到队尾,思考起来比较简单,n种情况直接比较即可。当然,看了三叶姐的题解,还可以用最小表示法,操作更少。
k > 1
这种情况重点讲一下,很多同学应该是这里不太好理解。题解给出的结论是,这种情况下总是可以构造出字典序最小的字符串,怎么证明呢?
我们先看最基础的情况,就是k=2。构造的方法如下:
- 将最大的字符移到对首
- 将最大的字符移到队尾
- 将第二大的字符移到对首,且最大的字符移到对首第二
- 讲对首2个字符打包,重复2-4步,直到全部有序 这么列出来可能还不太好理解,我们来个实走一遍,假设字符串为
b d a e c
- 每次移动对首1个字符到队尾,直到最大的e出现在对首
e c b d a
- 将已经有序的e打包,逐个移动到队尾
c b d a e
- 每次移动对首1个字符到队尾,直到第二大的d出现在对首
d a e c b
- 固定d不要动,每次移动第2个字符到队尾,直到e出现在d的后面
d e c b a
- 这样,de已经排好序了,打包成1个整体,逐个移动到队尾
c b a d e
- 此时,刚好c在对首了,如果不是c,可以逐个移动直到c到对首
c b a d e
- 固定对首的c不动,移动第2个字符,直到d出现在c后面,由于e紧跟d,所以此时cde有序
c d e b a
- cde已经排好序了,打包成1个整体,逐个移动到队尾
b a c d e
- 此时,刚好b在对首了,如果不是b,可以逐个移动直bc到对首
b a c d e
- 固定对首的b不动,移动第2个字符,直到c出现在b后面
b c d e a
- bcde已经排好序了,打包成1个整体,逐个移动到队尾
a b c d e
这样,排序就完成了。当k > 2时,我们可以也用这样的方式,只移动前2位到队尾,跟k = 2采取一样的策略。
Java版本代码
class Solution {
public String orderlyQueue(String s, int k) {
if (k == 1) {
String ans = s;
StringBuilder builder = new StringBuilder();
builder.append(s);
for (int i = 0; i < s.length() -1; i++) {
char c = builder.charAt(0);
builder.deleteCharAt(0);
builder.append(c);
if (builder.toString().compareTo(ans) < 0) {
ans = builder.toString();
}
}
return ans;
} else {
char[] chars = s.toCharArray();
Arrays.sort(chars);
return new String(chars);
}
}
}