单调栈解定长最小字典序问题
其中 1673 和 402、 1081 和 316 题只是换了说法而已,所以这里只有三道题。
- 316 去除重复字母(困难)
- 321 拼接最大数(困难)
- 402 移掉 K 位数字(中等)
- 1081 不同字符的最小子序列(中等)
- 1673 找出最具竞争力的子序列(中等)
402. 移掉K位数字
题目描述:
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
- num 的长度小于 10002 且 ≥ k。
- num 不会包含任何前导零。
示例 1 :
1 | 输入: num = "1432219", k = 3 |
示例 2 :
1 | 输入: num = "10200", k = 1 |
示例 3 :
1 | 输入: num = "10", k = 2 |
原理解析:
贪心算法:
对于两个相同长度的数字序列,最左边不同的数字决定了这两个数字的大小。
例如,对于 A = caxxx,B = cbxxx,如果 a > b 则 A > B。
基于此,我们可以知道,若要使得剩下的数字最小,需要保证靠前的数字尽可能小。
单调栈:
首先我们需要知道栈的特点,就是先进先出,这个特性保证剩下的数字的有序性。
使用栈这样的数据结构,且当栈顶的元素小于当前元素才能进栈,否则弹出栈顶的元素。
这样保证了栈底的元素是最小的
基于此,我们知道使用单调栈得到的数字字典序最小,但是可能剩余长度不满足要求
最大弹出数:为保证剩余长度满足要求,这里有一个栈总体的最大弹出数字的要求,最多能弹出K次。
逻辑与伪代码
1 | for 当前元素 in 序列: |
- 自左至右准备将当前元素存入栈中。
- 如果栈顶元素大于当前元素且当前可弹出次数> 0,则弹出栈顶元素,
- 重复步骤2,直至不满足步骤2条件。
- 将当前元素存入栈中。
- 回到步骤1。
动画解析
Python代码
1 | class Solution: |
注意要点
- 在判断栈顶元素是否大于当前进栈元素时,要先判断栈是否为空,否则会有空指针异常。
- 在结果输出时要注意输出的长度和栈为空时的输出值。
316. 去除重复字母
题目描述
给你一个字符串 s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
1 | 输入:s = "bcabc" |
示例 2:
1 | 输入:s = "cbacdcbc" |
提示:
1 <= s.length <= 104
s
由小写英文字母组成
原理解析
最大弹出数:为保证剩余每个字母都出现一次,需要统计每个字母的最大弹出数,使用哈希表来存储。
每个字母只出现一次:保证每个字母在栈中只出现一次,可以使用一个哈希表来存储栈中的对应元素。
逻辑与伪代码
1 | for 当前元素 in 序列: |
Python代码
1 | class Solution: |
321. 拼接最大数
题目描述
给定长度分别为 m
和 n
的两个数组,其元素由 0-9
构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n)
个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k
的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。
示例 1:
1 | 输入: |
示例 2:
1 | 输入: |
示例 3:
1 | 输入: |
原理解析
单调栈一分为二:两个单调栈的长度之和为合成的数组长度
k
,分别对两个数组进行上述两题的操作,获取两个单调栈。
将两个单调栈合并得到最终结果,对比不同的单调栈长度分配方法,获得最优的结果
使用归并排序中的
merge()
方法来合并,两个单调栈
动画解析
Python代码
1 | class Solution: |