力扣316-去除重复字母
一、原题题目
1.1 题目
给你一个字符串 s
,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同
1.2 示例
示例1:
输入: s = “bcabc”
输出: “abc”
示例2:
输入: s = “cbacdcbc”
输出: “acdb”
提示:
-
1 <= s.length <= 10^4
-
s
由小写英文字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicate-letters/
二、解题思路
2.1 题目意思理解
这里有个字典序概念可能大多数人都已经知道啦,咋们简单提一下即可。
字典序:是指按照单词出现在字典中的顺序去比较字符串的方法。例如 “abc” 的字典序在 “acdb” 的前面。
这题自己思考了很久,大概也有自己的思路,但实时起来太过于复杂,最终还是放弃,去看题解了。毕竟刷题嘛,时间有限,看题解也是快速掌握方法思路的方法,前提还是要自己有所思考。
看完官方题解后,顿时明白我自己思考的问题在哪里。官方题解里有下面三个数据结构来保存相关信息。官方题解
定义一个长度为26的数组会统计每个字符最后出现的位置,用于检测后面是否还存在某个字符的作用。
定义一个栈用于存需要选出的字符,会出现下面三种情况:
一:如果当前考虑的字符已经存在过则直接跳过这个字符。(代码12行)
二:如果当前考虑的字符就是排在栈顶字符字典序之后的,且在栈中没有存在过,那么这个字符就要入栈。(代码19行)
三:如果当前考虑的字符没有在栈中存在过,且是栈顶字符字典序之前的字符,那么就可能要考虑考虑栈顶的元素是否可以先丢弃,选择后面的那个字符来使整个字符串的字典序变小,这里就用到前面的数组,如果后面还有栈顶元素,则此时栈顶元素丢弃,如果后面没有,则不能丢弃,直接将当前元素入栈,这个过程是循环判断的过程。(代码14行)
定义一个长度为26的布尔数组用于存储是否在栈中存在了。
2.2 详细代码(Java)
public class Solution {
public String removeDuplicateLetters(String s) {
int len = s.length();
char[] chars = s.toCharArray();
int[] lastIntex = new int[26]; // 用于存储每个字符最后出现的位置
for (int i = 0;i<len;i++){ // 存储每个字符最后出现的位置
lastIntex[chars[i]-'a'] = i;
}
Deque<Character> stack = new ArrayDeque<>(); // 定义栈
boolean[] visited = new boolean[26]; // 定义是否存在于栈中的数组
for (int i = 0;i<len;i++){ // 遍历每一个字符
if (visited[chars[i]-'a']) continue; // 分析中的第一种情况
// 分析中的第三种情况
while (!stack.isEmpty() && chars[i]<stack.peekLast() && lastIntex[stack.peekLast()-'a']>i){
Character top = stack.removeLast();
visited[top-'a'] = false;
}
// 分析中的第二种情况
stack.addLast(chars[i]);
visited[chars[i]-'a'] = true;
}
// 以下是把栈中的结果保存成字符串输出
StringBuilder resstr = new StringBuilder();
for (Character ch : stack) {
resstr.append(ch);
}
return resstr.toString();
}
}
2.3 算法执行结果

三、总结分析
这道题自己有思考,虽然没想出来,但觉得每道题无论是否能做出来自己都需要有一个思考的过程,充分的思考可以让自己更容易理解别人的解题思路,印象也会更加深刻,然后自己记录下来供自己以后复习回顾。可能大家看了我上面所写的觉得很难懂,但这是在我的理解之上写的题解,我看到这些肯定能懂了,所以想要完全理解的小伙伴先自己有足够的思考再看官方题解我觉得就够了。