Using a Robot to Print the Lexicographically Smallest String

Using a Robot to Print the Lexicographically Smallest String

You are given a string s and a robot that currently holds an empty string t . Apply one of the following operations until s and t are both empty:

  • Remove the first character of a string s and give it to the robot. The robot will append this character to the string t.
  • Remove the last character of a string t and give it to the robot. The robot will write this character on paper.

Return the lexicographically smallest string that can be written on the paper.

Example 1:

Input: s = "zza"
Output: "azz"
Explanation: Let p denote the written string.
Initially p="", s="zza", t="".
Perform first operation three times p="", s="", t="zza".
Perform second operation three times p="azz", s="", t="".

Example 2:

Input: s = "bac"
Output: "abc"
Explanation: Let p denote the written string.
Perform first operation twice p="", s="c", t="ba". 
Perform second operation twice p="ab", s="c", t="". 
Perform first operation p="ab", s="", t="c". 
Perform second operation p="abc", s="", t="".

Example 3:

Input: s = "bdda"
Output: "addb"
Explanation: Let p denote the written string.
Initially p="", s="bdda", t="".
Perform first operation four times p="", s="", t="bdda".
Perform second operation four times p="addb", s="", t="".

Constraints:

1s.length105
s consists of only English lowercase letters.

 

解题思路

  可以发现字符串t的操作就是一个栈。因为是字典序最小,所以很容易想到先从s里面把所有的字母a挑出来,具体做法是先在s中找到字母a最后出现的位置p[a],然后从前往后枚举到p[a],如果遇到a,则直接把a输出,否则遇到其他字母则压入栈中。

  然后就是字母b,此时接着从上次遍历之后的位置p[a]+1开始,直到遍历到字母bs中最后出现的位置p[b]。在这之前需要将栈顶连续的部分中小于等于字母b的字符输出,然后再遍历s,如果遇到b,则直接把b输出,否则遇到其他字母则压入栈中。

  之后的字母以此类推。因此具体流程是枚举字母az,对于某个字母先将栈顶元素中小于等于该字母的元素弹出,然后从上一次的位置开始遍历s,如果遇到该字母则直接输出,否则压入栈中,直到遍历到该字母在s中出现的最后一个位置。

  AC代码如下:

复制代码
 1 class Solution {
 2 public:
 3     string robotWithString(string s) {
 4         int n = s.size();
 5         vector<int> p(26, -1);
 6         for (int i = 0; i < n; i++) {
 7             p[s[i] - 'a'] = i;
 8         }
 9         string ans, t;
10         for (int i = 0, j = 0; i < 26; i++) {
11             while (!t.empty() && t.back() <= 'a' + i) {
12                 ans += t.back();
13                 t.pop_back();
14             }
15             while (j < n && j <= p[i]) {
16                 if (s[j] == 'a' + i) ans += s[j];
17                 else t += s[j];
18                 j++;
19             }
20         }
21         reverse(t.begin(), t.end());
22         return ans + t;
23     }
24 };
复制代码

  当时比赛的时候想到的思路是,每当把一个元素压入栈时有两种选择,一种是出栈顶元素,另一种不弹出栈顶元素继续往后遍历。贪心的思路容易想到如果此时栈顶元素小于s后面剩余的字符,那么就应该把栈顶元素弹出。所有容易想到预处理出来每个位置后面有多少个字符是小于当前位置的字符的,每次弹出元素时,就把栈中大于栈顶元素的字符所对应的位置减1。然后发现这个信息并不好维护,基本只能暴力去枚举,所以比赛时没做出来。

  实际上问题的本质是看一下此时栈顶元素所对应的位置是不是对应后缀的最小元素,如果是则弹出栈顶元素。因此实际上只需预处理一个后缀最小值即可。

  AC代码如下:

复制代码
 1 class Solution {
 2 public:
 3     string robotWithString(string s) {
 4         int n = s.size();
 5         vector<char> p(n + 1, 'z');
 6         for (int i = n - 1; i >= 0; i--) {
 7             p[i] = min(p[i + 1], s[i]);
 8         }
 9         string ans;
10         vector<int> t;
11         for (int i = 0; i < n; i++) {
12             t.push_back(i);
13             while (!t.empty() && s[t.back()] <= p[i + 1]) {
14                 ans += s[t.back()];
15                 t.pop_back();
16             }
17         }
18         return ans;
19     }
20 };
复制代码

 

参考资料

  力扣第314场周赛:https://www.bilibili.com/video/BV1KR4y1R7vF/

posted @   onlyblues  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示