leetcode
leetcode 251 ~ 300
去除重复字母

去除重复字母

难度:

标签:

题目描述

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

 

示例 1:

输入:s = "bcabc"
输出"abc"

示例 2:

输入:s = "cbacdcbc"
输出:"acdb"

 

提示:

  • 1 <= s.length <= 104
  • s 由小写英文字母组成

 

注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同

代码结果

运行时间: 24 ms, 内存: 0.0 MB


// 使用Java Stream的解法
// 思路:
// 1. 计算每个字符最后出现的位置。
// 2. 通过字符串流过滤掉已经在结果字符串中的字符。
// 3. 通过reduce方法合并结果字符串。
 
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.Map;
import java.util.HashMap;
 
public class Solution {
    public String removeDuplicateLetters(String s) {
        // 计算每个字符最后出现的位置
        Map<Character, Integer> lastIndex = IntStream.range(0, s.length())
            .boxed()
            .collect(Collectors.toMap(i -> s.charAt(i), i -> i, (a, b) -> b));
 
        // 使用StringBuilder保存结果字符串
        StringBuilder result = new StringBuilder();
        s.chars().distinct().forEach(c -> {
            char ch = (char) c;
            while (result.length() > 0 && result.charAt(result.length() - 1) > ch && lastIndex.get(result.charAt(result.length() - 1)) > s.indexOf(ch)) {
                result.deleteCharAt(result.length() - 1);
            }
            result.append(ch);
        });
 
        return result.toString();
    }
}

解释

方法:

该题解使用了单调栈的思路。具体来说,遍历字符串 s 中的每个字符,如果当前字符已经在栈中,则直接跳过;否则,将栈顶元素与当前字符比较,如果栈顶元素大于当前字符且栈顶元素在后面还会出现,则弹出栈顶元素,直到栈为空或栈顶元素小于等于当前字符。然后将当前字符压入栈中。最后将栈中元素拼接成字符串作为结果返回。这样可以保证结果字符串的字典序最小。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中提到的`单调栈`是如何确保生成的字符串字典序最小的?
单调栈通过维持栈中元素的单调递增顺序来确保生成的字符串具有最小的字典序。在处理每个字符时,如果发现栈顶元素大于当前字符且栈顶元素在后面的字符串中还会再次出现,就将栈顶元素弹出。这样做可以保证在不破坏结果字符串中字符的唯一性的前提下,每次都尽可能用较小的字符替代较大的字符,从而使得最终形成的字符串字典序最小。
🦆
如果输入字符串`s`中包含所有26个英文字母,单调栈的最大可能高度是多少?
如果输入字符串`s`包含所有26个英文字母,则单调栈的最大可能高度是26。这种情况发生在字符串`s`已经按照某种字典序排列,且每个字符至少出现一次时,单调栈将可能包含所有这些字符,因为没有任何字符需要被弹出栈以保持字典序最小。
🦆
题解中提到,跳过栈中已有的字符是基于什么逻辑?为什么不需要再次考虑这些字符?
跳过栈中已有的字符是基于逻辑:一旦一个字符被加入到栈中,它就会出现在最终的结果字符串中,因为每个字符在结果中只能出现一次。通过使用`instack`哈希表来记录哪些字符已在栈中,可以确保不会重复处理这些字符。这样做是为了维持每个字符在结果字符串中的唯一性,避免重复添加导致的错误。
🦆
在弹出栈顶元素的条件中,需要检查栈顶元素在后面是否还会出现,这是如何通过`cnt`哈希表实现的?
通过`cnt`哈希表来跟踪每个字符在遍历过程中的剩余出现次数,可以决定是否可以安全地弹出栈顶元素。在每次遍历到一个字符时,会将其在`cnt`表中的计数减一。如果栈顶元素的计数大于0,表示该字符在字符串的后续部分还会再次出现,因此可以将其从栈中弹出以保持最小字典序。若计数为0,则表示栈顶元素不会再出现,不能弹出,以确保字符的出现至少一次。

相关问题