316. 去除重复字母

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了316. 去除重复字母相关的知识,希望对你有一定的参考价值。

402. 移掉 K 位数字(中等)

316. 去除重复字母

321. 拼接最大数

316. 去除重复字母

题目描述

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

示例 1:

输入: "bcabc"
输出: "abc"
示例 2:

输入: "cbacdcbc"
输出: "acdb"

思路

与上面题目不同,这道题没有一个全局的删除次数 k k k。而是对于每一个在字符串 s s s 中出现的字母 c c c 都有一个 k k k 值。这个 k k k c c c 出现次数 - 1。

沿用上面的知识的话,我们首先要做的就是计算每一个字符的 k k k,可以用一个字典来描述这种关系,其中 k e y key key 为 字符 c c c v a l u e value value 为其出现的次数。

具体算法:

  • 建立一个字典。其中 k e y key key 为 字符 c c c v a l u e value value 为其出现的剩余次数。
  • 从左往右遍历字符串,每次遍历到一个字符,其剩余出现次数 - 1.
  • 在弹出栈顶字符时,如果字符串在后面的位置上再也没有这一字符,则不能弹出栈顶字符。剩余数量为 0 时,就不能弹出栈顶字符了。
  • 是否丢弃的标准和上面题目类似。如果栈顶的元素字典序更大,那么我们选择丢弃栈顶的元素。

还记得上面题目的边界条件么?如果栈中剩下的元素大于 n − k n - k nk,我们选择截取前 n − k n - k nk 个数字。然而本题中的 k 是分散在各个字符中的,因此这种思路不可行的。

不过不必担心。由于题目是要求只出现一次。我们可以在遍历的时候简单地判断其是否在栈上即可。

class Solution:
    def removeDuplicateLetters(self, s) -> int:
        stack = []
        remain_counter = collections.Counter(s)
        for char in s:
            # c不在res中时再考虑是否添加
            if char not in stack:
                while stack and stack[-1] > char and remain_counter[stack[-1]] >0:
                    stack.pop()
                stack.append(char)
            remain_counter[char] -= 1
        return ''.join(stack)
s = "bcabc"
import collections
remain_counter = collections.Counter(s)
remain_counter
Counter({'b': 2, 'c': 2, 'a': 1})

复杂度分析

  • 时间复杂度:由于判断当前字符是否在栈上存在需要 O ( N ) O(N) O(N) 的时间,因此总的时间复杂度就是 O ( N 2 ) O(N ^ 2) O(N2),其中 N N N 为字符串长度。
  • 空间复杂度:我们使用了额外的栈来存储数字,因此空间复杂度为 O ( N ) O(N) O(N),其中 N N N 为字符串长度。

查询给定字符是否在一个序列中存在的方法。根本上来说,有两种可能:

  • 有序序列: 可以二分法,时间复杂度大致是 O ( N ) O(N) O(N)
  • 无序序列: 可以使用遍历的方式,最坏的情况下时间复杂度为 O ( N ) O(N) O(N)。我们也可以使用空间换时间的方式,使用 N N N的空间 换取 O ( 1 ) O(1) O(1)的时间复杂度。

由于本题中的 stack 并不是有序的,因此我们的优化点考虑空间换时间。而由于每种字符仅可以出现一次,这里使用 hashset 即可。

class Solution:
    def removeDuplicateLetters(self, s) -> int:
        stack = []
        seen = set()
        remain_counter = collections.Counter(s)
        for char in s:
            if char not in seen: # 空间换时间
                while stack and stack[-1] > char and remain_counter[stack[-1]] >0:
                    seen.discard(stack.pop())  # discard() 函数是用来删除元素的
                seen.add(char)
                stack.append(char)
            remain_counter[char] -= 1
        return ''.join(stack)

复杂度分析

  • 时间复杂度: O ( N ) O(N) O(N),其中 N N N 为字符串长度。
  • 空间复杂度:我们使用了额外的栈和 hashset,因此空间复杂度为 O ( N ) O(N) O(N),其中 N N N 为字符串长度。

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于316. 去除重复字母的主要内容,如果未能解决你的问题,请参考以下文章

leetcode——316. 去除重复字母

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

LeetCode 0316. 去除重复字母:单调栈

java 316.删除重复的字母(第1个).java

java 316.删除重复的字母(第1个).java

java 316.删除重复的字母(第1个).java