LeetCode每日一题打卡(3) 899. 有序队列

2022.8.3 每日一题补卡

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 只由小写字母组成。

解法

这题看起来过程复杂,但实际上是一道思维题,跟算法没啥关系。我首先考虑k=2的情况,此时本质上就是将相邻的两位字符两两比较,然后其中一个排到字符串末尾,其实移动到字符串末尾这个操作挺有迷惑性的,如果我们把字符串首尾相接形成一个环,想象一下从头开始比较的过程,是不是移动到字符串末尾就相当于是将这个字符排到这两个靠前的那个位置?形象一点的模拟过程如下:

a1 a2 a3 a4 a5 a6
^   ^
|   |
比较a1和a2,假设把a2移动到末尾

实际情形:
a1 a3 a4 a5 a6 a2

等价情形:
a2 a1 a3 a4 a5 a6

这时假设再把a3移动到末尾:

实际情形:
a1 a4 a5 a6 a2 a3

等价情形:
a2 a3 a1 a4 a5 a6

可以发现如果将字符串首尾相连形成一个环,实际情形和等价情形的环中各字符排列顺序是一样的,观察等价情形,很容易联想到一个排序过程——冒泡排序。我们利用这种形似冒泡的方式比较相邻两位,从头到尾将较小的项移动到前面,循环n次,就能形成一个有序序列。

因此这题所谓的将前k位中的一个字符移动到字符串末尾在k=2的情形下完全就可以使用类似冒泡排序的方法,我们每次将两位中字典序较小的放到前一位,较大的放到后一位,O(gif.latex?n%5E%7B2%7D)的复杂度就能生成一个字典序递增的字符串。所以k=2时所求最终的字符串就是原字符串各字符按字典序递增排列的结果,当k>2时同样都能按照k=2的方法操作,所以k>=2时都能得到字符按字典序排列的最优结果。

k=1时显然不一定能得到上述的最佳结果,这时直接模拟过程就行,时间复杂度O(n)。

代码

class Solution {
public:
    string orderlyQueue(string s, int k) {
        string ans;
        int len=s.size(),num[1010];
        if(k==1)
        {
            string now=s;
            ans=s; 
            for(int i=0;i<len;i++)
            {
                now=s.substr(1)+s[0];
                ans=min(now,ans);
                s=now;
            }
        }
        else
        {
            for(int i=0;i<len;i++)
            num[i]=s[i]-'a';
            sort(num,num+len);
            for(int i=0;i<len;i++)
            ans+=num[i]+'a';
        }
        return ans;
    }
};

代码思路

k=1时直接模拟,用substr实现字符串的拼接,ans记录最优解;k>=2时计算字典序递增序列,这里使用快速排序sort进一步优化,时间复杂度O(nlogn)。

总结

这题给的难度是hard,实际上并不难,不涉及任何高难度算法,只是比较考察思维吧,想通了和冒泡排序的相似之处就显得很简单了。

猜你喜欢

转载自blog.csdn.net/qq_43824745/article/details/126193788