从字符串中移除星号

标签: 字符串 模拟

难度: Medium

给你一个包含若干星号 * 的字符串 s

在一步操作中,你可以:

  • 选中 s 中的一个星号。
  • 移除星号 左侧 最近的那个 非星号 字符,并移除该星号自身。

返回移除 所有 星号之后的字符串

注意:

  • 生成的输入保证总是可以执行题面中描述的操作。
  • 可以证明结果字符串是唯一的。

示例 1:

输入:s = "leet**cod*e"
输出:"lecoe"
解释:从左到右执行移除操作:
- 距离第 1 个星号最近的字符是 "leet**cod*e" 中的 't' ,s 变为 "lee*cod*e" 。
- 距离第 2 个星号最近的字符是 "lee*cod*e" 中的 'e' ,s 变为 "lecod*e" 。
- 距离第 3 个星号最近的字符是 "lecod*e" 中的 'd' ,s 变为 "lecoe" 。
不存在其他星号,返回 "lecoe" 。

示例 2:

输入:s = "erase*****"
输出:""
解释:整个字符串都会被移除,所以返回空字符串。

提示:

  • 1 <= s.length <= 105
  • s 由小写英文字母和星号 * 组成
  • s 可以执行上述操作

Submission

运行时间: 104 ms

内存: 17.2 MB

class Solution:
    def removeStars(self, s: str) -> str:
        res=[]
        for c in s:
            if c=='*':
                res.pop()
            else:
                res.append(c)

        return ''.join(res)
        

Explain

此题解使用了一个栈(列表res)的结构来实现字符的暂存和删除操作。遍历字符串s中的每个字符:当遇到星号'*'时,从栈顶弹出一个元素,即删除星号左侧的非星号字符;当遇到非星号字符时,将其推入栈中。这样,栈中最终保存的就是所有未被星号删除的字符。最后,使用''.join()方法将栈中的字符合并成一个字符串作为最终结果。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def removeStars(self, s: str) -> str:
        res = []  # 初始化一个空栈来存放字符
        for c in s:  # 遍历字符串中的每个字符
            if c == '*':
                res.pop()  # 如果当前字符是星号,则从栈中弹出一个元素
            else:
                res.append(c)  # 如果是非星号字符,则压入栈中

        return ''.join(res)  # 将栈中的字符合并成一个字符串返回

Explore

栈是一种后进先出(LIFO)的数据结构,非常适合处理与最近相关的操作。在这个问题中,每当遇到星号`*`时,我们需要删除其左侧最近的一个非星号字符。栈可以让我们方便地访问到最后插入的元素,并在遇到星号时迅速移除它,从而有效地处理字符的添加与删除,保证时间复杂度为线性O(n)。

在题解中未明确检查栈是否为空就直接使用`res.pop()`。理论上,如果`s`以星号`*`开始,这将尝试从一个空栈中弹出元素,导致运行时错误。因此在实际实现中,应当添加一个检查以确保栈不为空再执行`pop`操作,避免错误。

在题解的原始实现中,确实没有检查栈是否为空就直接执行了`res.pop()`。这种做法默认字符串中的星号数量不会超过非星号字符的数量,是基于题目背景或已知的输入约束。然而,为了代码的健壮性,最好在执行`pop`前检查栈是否为空,以避免因弹出空栈而导致错误。

在这种方法中,每个字符最多被处理两次——一次是被推入栈中,另一次可能是从栈中弹出。因此,总的操作次数(包括`push`和`pop`)最多是字符串长度的两倍,不会超过字符串长度。整体的时间复杂度维持在O(n)。