leetcode
leetcode 801 ~ 850
有序队列

有序队列

难度:

标签:

题目描述

代码结果

运行时间: 26 ms, 内存: 16.9 MB


/*
 * 思路:
 * 1. 当 k == 1 时,我们只能将字符串的第一个字符移动到末尾,这样等价于旋转字符串。
 *    因此,我们可以尝试所有的旋转,找到字典序最小的那个。
 * 2. 当 k > 1 时,我们可以对字符串进行任意排序。
 *    因此,我们只需对字符串进行排序即可。
 */
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Solution {
    public String orderlyQueue(String s, int k) {
        if (k == 1) {
            return IntStream.range(0, s.length())
                    .mapToObj(i -> s.substring(i) + s.substring(0, i))
                    .min(String::compareTo)
                    .orElse(s);
        } else {
            return s.chars()
                    .sorted()
                    .mapToObj(c -> String.valueOf((char) c))
                    .collect(Collectors.joining());
        }
    }
}

解释

方法:

这个题解的思路是分两种情况讨论: 1. 当k=1时,我们只能选择队列的第一个字符,然后将其移到队列的末尾。这等价于对字符串进行旋转操作。我们将字符串s所有可能的旋转形式存储在数组a中,然后对数组a进行排序,取排序后的第一个字符串即为最优解。 2. 当k>1时,因为我们可以选择前k个字符中的任意一个,相当于可以对前k个字符进行任意排列。所以无论我们进行多少次操作,最终得到的字符串在字典序上一定不会小于将原字符串按字典序排列得到的字符串。所以我们直接对原字符串的字符进行排序即可得到最优解。

时间复杂度:

O(n^2)

空间复杂度:

O(n^2)

代码细节讲解

🦆
为什么在k=1的情况下,所有可能的旋转字符串排序后的第一个元素就是字典序最小的结果?
在k=1的情况下,我们的操作仅限于将字符串的首个字符移动到末尾,这种操作实际上生成了字符串的所有可能的旋转版本。每次操作后的结果都是原字符串的一个旋转形式,因此,对于一个长度为n的字符串,它有n种可能的旋转形式。例如,字符串's'的所有旋转包括其自身及其所有可能的旋转。当这些旋转形式被放入数组并进行排序时,数组的第一个元素自然是这些旋转中字典序最小的。这是因为排序本质上是将所有元素按字典序排列,而数组中的第一个元素就是所有可能旋转中字典序最小的。
🦆
在k>1的情况下,直接对字符串进行排序得到的结果为什么一定是最优解?是否有可能通过特定的字符移动得到更小的字典序?
当k>1时,我们可以从字符串的前k个字符中选择任意一个来移动到字符串的末尾。这实际上允许我们对前k个字符进行任意排列,进而通过多次操作,可以改变整个字符串的顺序。由于我们可以自由排列前k个字符,通过足够的操作,我们可以实现原字符串的任何排列。因此,这意味着我们可以通过选择合适的操作顺序来重新排列整个字符串。所以,直接将原字符串的所有字符按照字典序排序,得到的结果就是我们通过任意次操作可能得到的字典序最小的字符串。不存在其他特定的字符移动方式能得到更小的字典序结果,因为排序已使得每个字符都尽可能地处于其在字典序上可能的最前位置。
🦆
在k=1时,能否使用更高效的方法找到字典序最小的字符串,而不是生成所有旋转后的字符串并排序?
在k=1的情况下,可以使用一个更高效的算法来寻找字典序最小的旋转字符串,而不必生成所有旋转形式并进行排序。这种方法通常基于字符串的双倍方法和最小表示法。具体做法是,将字符串s重复一次形成新的字符串s+s,这样做的目的是在不断旋转的过程中捕获所有可能的旋转字符串。然后,通过比较每个可能的旋转的起始位置,找到能够生成字典序最小字符串的起始位置。这个起始位置可以使用高效的字符串比较算法(比如最小表示法)来找到。这种方法的时间复杂度通常优于生成所有旋转形式并排序的方法,因为它直接在连续的内存块上进行操作,并且利用了字符串比较的高效性。

相关问题