题目
899. 有序队列
给定一个字符串 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 只由小写字母组成。
解法
方法1:最小表示法
-
当k=1时,即求字符串的最小表示法
-
当k>1时,每次可以调整开头的的至少两个字符的位置,如bazzz-> bzzza -> zzzab …->abzzz,对于在排序算法中,值需要交换相邻的两个字符可以实现序列有序,所以这种情况下只需要字符串排序即可
-
当 K = 2 时,可以发现,我们能够交换字符串中任意两个相邻的字母。具体地,设字符串 S 为 S[1], S[2], …, S[i], S[i + 1], …, S[N],我们需要交换 S[i] 和 S[j]。首先我们依次将 S[i] 之前的所有字符依次移到末尾,得到
S[i], S[i + 1], …, S[N], S[1], S[2], …, S[i - 1]
随后我们先将 S[i + 1] 移到末尾,再将 S[i] 移到末尾,得到
S[i + 2], …, S[N], S[1], S[2], …, S[i - 1], S[i + 1], S[i]
最后将 S[i + 1] 之后的所有字符依次移到末尾,得到
S[1], S[2], …, S[i - 1], S[i + 1], S[i], S[i + 2], …, S[N]
这样就交换了 S[i] 和 S[i + 1],而没有改变其余字符的位置。
当我们可以交换任意两个相邻的字母后,就可以使用冒泡排序的方法,仅通过交换相邻两个字母,使得字符串变得有序。因此当 K = 2 时,我们可以将字符串移动得到最小的字典序。
当 K > 2 时,我们可以完成 K = 2 时的所有操作。
public String orderlyQueue(String s, int k) {
if (k == 1) return getMin(s);
char[] ch = s.toCharArray();
Arrays.sort(ch);
return String.valueOf(ch);
}
public String getMin(String s) {
int n = s.length();
s = s + s;
int i = 0, j = 1;
while (i < n && j < n) {
int p = 0;
while (p < n && s.charAt(i + p) == s.charAt(j + p)) p++;
if (s.charAt(i + p) > s.charAt(j + p)) {
i += p + 1;
} else {
j += p + 1;
}
if (i == j) j++;
}
int q = Math.min(i, j);
return s.substring(q, q + n);
}