-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
96 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
--- | ||
layout: post | ||
title: 单调栈变体-删除重复字符使得字典序最小 | ||
date: 2024-07-11 11:00:23 | ||
excerpt_type: html # <!-- more -->标签渲染方式 | ||
updated: 2024-07-11 | ||
categories: 算法记录 | ||
tags: | ||
- Java | ||
- 算法 | ||
- 单调栈 | ||
- 力扣 | ||
--- | ||
|
||
## 316.去除重复字母 | ||
|
||
[316.去除重复字母](https://leetcode.cn/problems/remove-duplicate-letters/description/?envType=study-plan-v2&envId=2024-spring-sprint-100) | ||
|
||
除你字母。 | ||
|
||
<!-- more --> | ||
|
||
谁家中等题2185分。 | ||
|
||
这一题是单调栈的变体应用。一般使用单调栈是为了在一次遍历中找到整个数组中所有元素的**右边第一个比它大/小的元素**,此时在元素出栈时就能够记录这个第一个比它大/小的元素。 | ||
|
||
在本题中,单调栈的作用仅仅是为了维护一个递增序列,入栈的元素在最终作为答案使用,当元素出栈时也只是代表不使用这个元素。 | ||
|
||
为了维护单调递增序列,使用一个从栈底到栈顶递增的栈。 | ||
|
||
当元素将要入栈时,首先检查栈内是否已有相同元素,如果有则无需再入栈(否则需要将栈顶到那个相同元素的位置全都出栈,如果这样做能够得到更优解的话说明之后出现了比栈内元素字典序更小的元素,那么不如将出栈的任务交给那个更小的元素。) | ||
|
||
当比栈顶更小的元素为了入栈要让栈顶元素出栈前,先检查剩余字母中是否还有待出栈元素,如果没有的话则不出栈,后来者直接入栈即可。 | ||
|
||
在代码实现时,使用一个`set`时刻记录栈中是否存在某字母,并且使用一个`Map`记录剩余没有扫描到的字母数量(其实在这一题中使用数组就可以了)。 | ||
|
||
代码(java): | ||
|
||
```java | ||
class Solution { | ||
public String removeDuplicateLetters(String s) { | ||
int n = s.length(); | ||
Deque<Integer> stack = new LinkedList<>(); | ||
Set<Character> set = new HashSet<>(); | ||
Map<Character, Integer> map = new HashMap<>(); | ||
for(int i=0;i<n;i++){ | ||
if(!map.containsKey(s.charAt(i))){ | ||
map.put(s.charAt(i),1); | ||
} | ||
else{ | ||
map.put(s.charAt(i),map.get(s.charAt(i))+1); | ||
} | ||
} | ||
for(int i=0;i<n;i++){ | ||
//如果栈内存在当前字母,则直接将其跳过 | ||
if(set.contains(s.charAt(i))){ | ||
if(map.get(s.charAt(i))>1){ | ||
map.put(s.charAt(i), map.get(s.charAt(i))-1); | ||
} | ||
else{ | ||
map.remove(s.charAt(i)); | ||
} | ||
continue; | ||
} | ||
//出栈 | ||
while(stack.size() != 0 && s.charAt(stack.peek())>=s.charAt(i) && map.containsKey(s.charAt(stack.peek()))){ | ||
int index = stack.pollFirst(); | ||
|
||
set.remove(s.charAt(index)); | ||
} | ||
//入栈 | ||
if(stack.size() == 0 || !set.contains(s.charAt(i))){ | ||
stack.offerFirst(i); | ||
set.add(s.charAt(i)); | ||
} | ||
//将“剩余字母”去掉当前字母 | ||
if(map.get(s.charAt(i))>1){ | ||
map.put(s.charAt(i), map.get(s.charAt(i))-1); | ||
} | ||
else{ | ||
map.remove(s.charAt(i)); | ||
} | ||
} | ||
StringBuilder sb = new StringBuilder(); | ||
while(stack.size()>0){ | ||
sb.append(s.charAt(stack.pollLast())); | ||
} | ||
return sb.toString(); | ||
} | ||
} | ||
``` | ||
|
||
时间复杂度:$O(n)$,仅遍历一遍。 | ||
空间复杂度:$O(|Σ|)$,即所有可能的字符种类数,最坏情况是所有字母都只出现一次,Map的大小即为字符数量。 |