Return the lexicographically smallest subsequence of s
that contains all the distinct characters of s
exactly once.
Note: This question is the same as 316: https://leetcode.com/problems/remove-duplicate-letters/
Example 1:
Input: s = "bcabc"
Output: "abc"
Example 2:
Input: s = "cbacdcbc"
Output: "acdb"
Constraints:
1 <= s.length <= 1000
s
consists of lowercase English letters.
這道題跟之前那道 Remove Duplicate Letters 一模一樣,這已經不是第一次遇到這種情況了,博主就納悶了怎么 LeetCode 就沒有個查重系統呢,強行讓我們復習以前做過的題目嗎。這道題讓找出字母順序最小的一個子序列,使得所有的不同的字母只出現一次,之前那道題說的是去掉重復的字母使得每個字母只出現一次,且剩下的是字母順序最小的一個。其實二者說的都是一個東西,這道題實際上需要用單調棧的思路來做,首先需要統計每個字母出現的次數,這里可以使用一個大小為 128 的數組 cnt 來表示,還需要一個數組 visited 來記錄某個字母是否出現過。先遍歷一遍字符串,統計每個字母出現的次數到 cnt 中。再遍歷一遍給定的字符串,對於遍歷到的字母,在 cnt 數組中減去一個,然后看該字母是否已經在 visited 數組中出現過,是的話直接跳過。否則需要進行一個 while 循環,這里的操作實際上是為了確保得到的結果是字母順序最小的,若當前字母小於結果 res 中的最后一個字母,且該最后的字母在 cnt 中還存在,說明之后還會遇到這個字母,則可以在 res 中先去掉這個字母,以保證字母順序最小,並且 visited 數組中標記為0,表示未訪問。這里是盡可能的將 res 打造成單調遞增的,但如果后面沒有這個字母了,就不能移除,所以說並不能保證一定是單調遞增的,但可以保證得到的結果是字母順序最小的。while 循環退出后,將該字母加到結果 res 后,並且 visited 標記為1。這里還有個小 trick,結果 res 在初始化給個0,這樣就不用判空了,而且0是小於所有字母的,不會影響這個邏輯,最后返回的時候去掉首位0就行了,參見代碼如下:
class Solution {
public:
string smallestSubsequence(string s) {
string res = "0";
vector<int> cnt(128), visited(128);
for (char c : s) ++cnt[c];
for (char c : s) {
--cnt[c];
if (visited[c]) continue;
while (c < res.back() && cnt[res.back()]) {
visited[res.back()] = 0;
res.pop_back();
}
res += c;
visited[c] = 1;
}
return res.substr(1);
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1081
類似題目:
參考資料:
https://leetcode.com/problems/smallest-subsequence-of-distinct-characters/