↓快轉到主要內容
  1. LeetCode/

LeetCode 3816: Lexicographically Smallest String After Deleting Duplicate Characters

·1 分鐘· ·
LeetCode Hard Stack Monotonic-Stack Greedy String
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-01-18

  • 提交紀錄:Wrong Answer ×5(01-18)→ RE、WA、RE、WA → Accepted → Accepted(07-27)

解題思路
#

可以刪除「目前出現至少兩次」的字母的任何一個,讓字串字典序最小。跟 LC 316 很像,差別是:每個字母至少要留一個,但不必剛好一個。

用 cnt[ch] 記錄「目前字串裡 ch 還有幾個」(stack 裡的加上還沒處理的)。從左到右:

  1. 看到 c 時,如果 stack 頂端比 c 大,而且 cnt[頂端] > 1(還有別的副本),就刪掉它,cnt 減 1。
  2. 把 c 推進 stack。

處理完之後還有一步:尾巴的字母如果還有別的副本,也要刪掉。因為一個字串的前綴,字典序一定比它自己小,例如 "ab" < "abb"。

我的嘗試
#

一月:從右往左刪,五次 WA
#

一開始想從右往左掃,記錄要刪掉的 index,最後再組回字串。邏輯一直沒有對應到「前面的大字母遇到後面的小字母就該刪」,五次 WA。

七月:單調棧,但記錄的東西太多
#

改成單調棧,同時維護 count(還沒處理的)和 seen(stack 裡的)兩個計數。中間兩次 Runtime Error 是先 pop 才去讀 stack[-1],順序寫反;兩次 WA 是最後刪尾巴時,只檢查了原本的最後一個字元,沒有在刪掉之後更新成新的尾巴。

最後一版只用一個 cnt:它代表「目前字串裡這個字母還有幾個」,刪除時減 1,就同時處理了 stack 裡和後面的副本。

正確解法
#

from collections import Counter

class Solution:
    def lexSmallestAfterDeletion(self, s: str) -> str:
        cnt = Counter(s)
        stack = []
        for ch in s:
            while stack and stack[-1] > ch and cnt[stack[-1]] > 1:
                cnt[stack.pop()] -= 1
            stack.append(ch)
        while cnt[stack[-1]] > 1:                 # 尾巴重複的也刪掉
            cnt[stack.pop()] -= 1
        return "".join(stack)

跟 LC 316 的差別
#

LC 316LC 3816
要求每個字母剛好一個每個字母至少一個
可以刪的條件後面還會出現目前還有不只一個
已在 stack 裡的字母跳過照樣推進去
結尾不用處理尾巴重複的要刪掉

複雜度
#

時間 O(n),空間 O(n)。

要特別避免的錯誤
#

  • 先 pop 再讀 stack[-1]:讀到的是新的頂端,而且 stack 可能已經空了。
  • 刪尾巴時沒有每次都重新看新的尾巴。
  • 套用 LC 316 的寫法:這題不需要 seen,而且結尾要多刪一步。

面試口說整理
#

我用 cnt 記錄每個字母目前還有幾個,從左到右用單調棧建答案:頂端比目前字母大、而且還有別的副本時就刪掉它,cnt 減 1。全部處理完之後,尾巴的字母如果還有別的副本也刪掉,因為前綴的字典序一定更小。每個字元最多進出一次,時間 O(n)。

相關文章

LeetCode 402: Remove K Digits
·1 分鐘
LeetCode Medium String Stack Greedy Monotonic-Stack
LeetCode 402 解題筆記:單調遞增 stack,遇到比頂端小的數字就把頂端刪掉(還有 k 次可刪時),最後去掉前導零
LeetCode 84: Largest Rectangle in Histogram
·1 分鐘
LeetCode Daily Hard Array Stack Monotonic-Stack Dynamic-Programming Geometry Histogram
使用單調堆疊解決直方圖中最大矩形面積問題
LeetCode 2781: Length of the Longest Valid Substring
·2 分鐘
LeetCode Hard Sliding-Window Hash-Table String Trie
LeetCode 2781 解題筆記:禁字最長只有 10,每個右端只檢查最後 10 個字元組成的後綴;找到禁字就把左端推到它後面,要找最短的那個(最靠右的起點)
LeetCode 2156: Find Substring With Given Hash Value
·1 分鐘
LeetCode Hard Sliding-Window Rolling-Hash String
LeetCode 2156 解題筆記:雜湊的第一個字元乘 p^0,從右往左滑動時只要乘 p 再加新字元、扣掉 p^k 倍的出去字元,不需要除法;modulo 不一定是質數,不能用模反元素
LeetCode 2953: Count Complete Substrings
·2 分鐘
LeetCode Hard Sliding-Window Hash-Table String
LeetCode 2953 解題筆記:先在相鄰字元差 > 2 的地方切段;每段枚舉有幾種字元 d,視窗長度固定是 d × k,維護「剛好出現 k 次的字元數」
LeetCode 2131: Longest Palindrome by Concatenating Two Letter Words
·1 分鐘
LeetCode Medium Greedy Hash-Table String Counting
LeetCode 2131 解題筆記:每個字跟它的反轉配成一對放兩邊;本身是回文的字也能兩兩配對,只有剩下的一個能放正中間