LeetCode 316. Remove Duplicate Letters
问题描述
给你一个字符串 s,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
解题思路
本题的本质是:在保持原字符串中每个字符相对顺序的前提下,选出一个字典序最小的子序列,且每个字符恰好出现一次。这是经典的单调栈 + 贪心问题。
核心思路
统计每个字符的出现次数:用
count[26]数组记录每个字符在剩余字符串中还剩多少个。这是判断”能否丢弃当前字符”的关键依据。维护单调递增栈:遍历字符串,对于每个字符
c:- 将
count[c]减 1 - 如果
c已经在栈中,跳过(确保每个字符只出现一次) - 如果
c不在栈中,检查栈顶字符top:- 如果
top > c(栈顶字符字典序大于当前字符)且count[top] > 0(后续还会出现top),则可以将top弹出。因为我们可以后面再添加top,这样能让结果字典序更小 - 重复以上检查,直到栈为空或栈顶不能再弹出
- 如果
- 将
c压入栈
- 将
结果输出:栈中从底到顶就是最终结果的逆序,反转后得到答案。
举例说明
以输入 s = "bcabc" 为例:
| 步骤 | 当前字符 | count 状态 | 栈状态 | 说明 |
|---|---|---|---|---|
| 初始化 | - | b:4, c:2, a:1 | [] | |
| i=0 | ‘b’ | b:3, c:2, a:1 | [b] | 栈空,直接压入 |
| i=1 | ‘c’ | b:3, c:1, a:1 | [b,c] | c > b,压入 |
| i=2 | ‘a’ | b:3, c:1, a:0 | [a] | a < c 且 c 后面还有(c:1>0),弹出 c;a < b 且 b 后面还有(b:3>0),弹出 b;压入 a |
| i=3 | ‘b’ | b:2, c:1, a:0 | [a,b] | b > a,压入 |
| i=4 | ‘c’ | b:2, c:0, a:0 | [a,b,c] | c > b,压入 |
结果:"abc",字典序最小。
为什么用单调栈?
贪心策略:对于相邻的两个字符,如果前一个字符字典序更大,且它之后还会再出现,那么丢弃它能让结果字典序更小。这天然适合用单调递增栈来维护。
复杂度分析
- 时间复杂度:O(n),每个字符最多入栈出栈一次。
- 空间复杂度:O(1),虽然使用了栈和计数数组,但它们的大小仅取决于字符集大小(26 个小写字母),是常数级别。
代码
1 | package code.J316; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 𝒞𝒶𝓃𝒶𝓇𝓎!