1910. 删除一个字符串中所有出现的给定子字符串

字符串反复删除子串问题的多种解法解析

✨题目描述

给定两个字符串 spart,我们需要对 s 执行以下操作,直到 s 中不再包含任何子串 part

每次找到 s 中最左边出现的 part 子串,并将其从 s 中删除。

最后,返回所有 part 都被删除后的最终字符串。

注意:

  • 子串指的是字符串中连续的字符序列。
  • 删除操作是从左到右,每次只删除最左边的一个匹配部分。

示例说明

示例 1:

输入:s = "daabcbaabcbc", part = "abc"
输出:"dab"

解释:
- 删除 "abc"(下标 2)→ "dabaabcbc"
- 删除 "abc"(下标 4)→ "dababc"
- 删除 "abc"(下标 3)→ "dab"

示例 2:

输入:s = "axxxxyyyyb", part = "xy"
输出:"ab"

解释:
- 删除 "xy"(下标 4)→ "axxxyyyb"
- 删除 "xy"(下标 3)→ "axxyyb"
- 删除 "xy"(下标 2)→ "axyb"
- 删除 "xy"(下标 1)→ "ab"

解题思路分析

这个问题的核心在于:

  • **反复查找并删除子串 ****part**
  • **删除时优先匹配最左边的 ****part**
  • 直到 **s** 中不再包含 **part** 为止

我们可以使用两种主要方法来实现这个过程:

方法一:使用 str.replace() + while 循环

每次使用 s.replace(part, "", 1),只替换最左边的一个子串,配合 while part in s 来不断迭代。

方法二:使用“栈”模拟删除过程(一次遍历)

通过遍历字符串 s,将字符逐一压入栈中,同时检查栈尾是否形成了 part,一旦匹配,就从栈中删除对应部分。这种方法效率更高,不需要反复查找。


解题方法与代码实现

✅方法一:暴力查找与替换

class Solution:
    def removeOccurrences(self, s: str, part: str) -> str:
        while part in s:
            s = s.replace(part, "", 1)  # 只替换最左边的一个 part
        return s
⏱️时间复杂度
  • 最坏情况为 O(N * M),其中 N 是 s 的长度,M 是 part 的长度。
  • 每次查找可能需要 O(N),而最多需要删除 N/M 次。
✅优点
  • 简单易懂,语义清晰。
  • 适合初学者使用。
❌缺点
  • 多次扫描字符串,性能较差。

方法二:使用栈提高效率

class Solution:
    def removeOccurrences(self, s: str, part: str) -> str:
        stack = []
        part_len = len(part)

        for ch in s:
            stack.append(ch)
            if len(stack) >= part_len and ''.join(stack[-part_len:]) == part:
                del stack[-part_len:]

        return ''.join(stack)
⏱️时间复杂度
  • O(N),只需遍历一次字符串 s
  • 每个字符最多进栈一次、出栈一次。
✅优点
  • 效率高,适合长字符串处理。
  • 解决了暴力法中多次查找的问题。
❌缺点
  • 需要额外的栈空间。
  • 实现上比方法一略复杂。

方法对比分析

方法 时间复杂度 空间复杂度 易理解性 性能表现
替换 + while O(N * M) O(1) ⭐⭐⭐⭐ ⭐⭐
栈模拟法 O(N) O(N) ⭐⭐⭐ ⭐⭐⭐⭐

总结

字符串反复删除子串的问题虽然不难,但背后其实隐藏着不少值得深入思考的点:

  • 如何高效查找并删除子串?
  • 如何避免重复遍历?
  • 是选择语义直观的 API 方式,还是手动模拟底层逻辑以换取性能?

如果面对较短的字符串或对执行效率不敏感,str.replace() 的方式已经足够使用。但若处理大规模字符串、或要求在性能敏感场景下运行,推荐使用栈模拟法,它可以将时间复杂度从 O(NM) 降为 O(N)。

你可能感兴趣的:(python,算法,leetcode,开发语言,python)