力扣每日一题0803-899. 有序队列

103 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

给定一个字符串 s 和一个整数 k 。你可以从 s 的前 k 个字母中选择一个,并把它加到字符串的末尾。

返回 在应用上述步骤的任意数量的移动后,字典上最小的字符串 。

示例 1:

输入:s = "cba", k = 1
输出:"acb"
解释:
在第一步中,我们将第一个字符(“c”)移动到最后,获得字符串 “bac”。
在第二步中,我们将第一个字符(“b”)移动到最后,获得最终结果 “acb”。

示例 2:

输入:s = "baaca", k = 3
输出:"aaabc"
解释:
在第一步中,我们将第一个字符(“b”)移动到最后,获得字符串 “aacab”。
在第二步中,我们将第三个字符(“c”)移动到最后,获得最终结果 “aaabc”。

分情况讨论

计算字典序最小的字符串时,需要分别考虑 k=1k=1k>1k>1 的两种情况。

k=1k=1 时,每次只能取 ss 的首个字符并将其移动到末尾,因此对于给定的字符串,可能的移动方法是唯一的,移动后的结果也是唯一的。对于长度为 nn 的字符串 ss,经过 00 次到 n1n−1 次移动之后分别得到 nn 个字符串,这 nn 个字符串中的字典序最小的字符串即为答案。

k>1k>1 时,一定可以经过移动将 ss 变成升序字符串,因此将字符串 ss 升序排序之后得到的字符串即为答案。理由如下。

考虑 k=k= 的情况。假设 ss 的所有字符按照升序排序依次是 c0,c1,,cn1c_0, c_1, \ldots, c_{n - 1}。对于 ss 的任意排列,总是可以经过若干次移动将 cn1c_{n - 1} 变成首个字符。

cn1c_{n - 1} 变成首个字符之后,可以将 cn2,cn1c_{n - 2}, c_{n - 1} 变成前两个字符:

  1. 每次将首个字符移动到末尾,直到 cn2c_{n - 2} 变成首个字符;
  2. 保持 cn2c_{n - 2} 位于首个字符,每次将 cn2c_{n - 2} 后面的字符移动到末尾,直到 cn2c_{n - 2} 后面的字符是 cn1c_{n - 1}

使用同样的方法,对于 1m<n1 \le m < n,如果cnm,cnm+1,,cn1c_{n - m}, c_{n - m + 1}, \ldots, c_{n - 1} 位于前 mm 个字符,则可以经过若干次移动将 cnm1,cnm,cnm+1,,cn1c_{n - m - 1}, c_{n - m}, c_{n - m + 1}, \ldots, c_{n - 1} 变成前 m+1m+1 个字符:

  1. 每次将首个字符移动到末尾,直到 cnm1c_{n - m - 1} 变成首个字符,此时 cnm,cnm+1,,cn1c_{n - m}, c_{n - m + 1}, \ldots, c_{n - 1} 为字符串中连续的 mm 个字符;
  2. 保持 cnm1c_{n - m - 1} 位于首个字符,每次将 cnm1c_{n - m - 1} 后面的字符移动到末尾,直到 cnm1c_{n - m - 1} 后面的字符是 cnmc_{n - m},此时前 m+1m+1 个字符是 cnm1,cnm,cnm+1,,cn1c_{n - m - 1}, c_{n - m}, c_{n - m + 1}, \ldots, c_{n - 1}

因此,当 k=2k=2 时,一定可以经过移动将 ss 变成升序字符串。

k>2k>2 时,同样可以对字符串的前两个字符执行移动操作将 ss 变成升序字符串。

var orderlyQueue = function(s, k) {
    if (k === 1) {
        let ans = s;
        for (let i = 0; i < s.length - 1; ++i) {
            const n = s.length;
            s = s.substring(1, n) + s[0];
            ans = ans < s ? ans : s;
        }
        return ans;
    }
    return [...s].sort().join('');
};