LeetCode C++ 316. Remove Duplicate Letters【Stack/Greedy/String】中等

memcpy0發表於2020-12-24

Given a string s, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.

Example 1:

Input: s = "bcabc"
Output: "abc"

Example 2:

Input: s = "cbacdcbc"
Output: "acdb"

Constraints:

  • 1 <= s.length <= 10^4
  • s consists of lowercase English letters.

題意:給出一個字串 s ,去除字串中重複的字母,使得每個字母只出現一次。需保證 返回結果的字典序最小(要求不能打亂其他字元的相對位置)。


解法 貪心+棧

本題的做法有一點麻煩,因為題目要我們完成的任務比較多:

  • 去重:保留每種字元各一個;
  • 最小字典序:這些字元構成的字串其字典序最小;
  • 保持順序:保留的字元之間的順序不變。

因此不是一個單調棧就能夠解決的事,儘管和單調棧的思路有一點相似——單調上升的字元序列,其字典序最小。在程式中,我們將要維護的這個棧不完全是單調的,比如說 "bac" ,當我們遍歷到下標 2 時,棧中含有 ba ,這不是單調上升的。

最終的做法是:遇到一個已經出現的字元,就跳過;遇到一個新字元,如果小於棧頂元素,並且在字串(新字元的)後面還有同樣的棧頂字元,就不斷彈出棧頂字元,之後入棧新字元。比如 ""bcabc" ,遍歷到下標 2 時,就要彈出前面的 b, c 字元,然後入棧 a 字元。

實際程式碼如下:

class Solution {
public:
    string removeDuplicateLetters(string s) {
        vector<char> st;
        int n = s.size();
        bool vis[26] = {false};
        for (int i = 0; i < n; ++i) {
            if (vis[s[i] - 'a']) continue; //棧中已經含有這一字元
            //遇到一個新字元,如果小於棧頂,並且新字元後面還有和棧頂一樣的,就彈出棧頂字元
            while (!st.empty() && st.back() > s[i] && s.find(st.back(), i) != string::npos) {
                vis[st.back() - 'a'] = false;
                st.pop_back();
            }
            vis[s[i] - 'a'] = true;
            st.push_back(s[i]);
        }
        string ans;
        for (const char &c : st) ans += c;
        return ans;
    }
};

提交後的結果如下:

執行用時:4 ms, 在所有 C++ 提交中擊敗了74.17% 的使用者
記憶體消耗:6.5 MB, 在所有 C++ 提交中擊敗了96.64% 的使用者

相關文章