301 删除无效的括号(dfs减枝)

1. 问题描述:

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按任意顺序返回。

示例 1:

输入:s = "()())()"
输出:["(())()","()()()"]

示例 2:

输入:s = "(a)())()"
输出:["(a())()","(a)()()"]

示例 3:

输入:s = ")("
输出:[""]

提示:

1 <= s.length <= 25
s 由小写英文字母以及括号 '(' 和 ')' 组成
s 中至多含 20 个括号

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-invalid-parentheses

2. 思路分析:

① 首先合法的括号序列满足两点:一是左右括号的数量相等;二是任意一个括号序列的前缀中左括号的数目大于等于右括号的数量。因为需要求解出所以可能的括号序列所以肯定需要dfs搜索所有的可能性(递归搜索所有的可能性),但是直接搜索肯定会超时所以我们需要减枝来降低时间复杂度。我们可以利用上面说到的合法括号序列预处理一下,计算给出的括号序列中能够删除的最少的左括号数目l与右括号的数目r,在递归的时候我们就可以根据当前可以删除的最少的左括号数目与右括号数目进行减枝。因为可能存在多个连续的左括号或者是右括号,所以我们不能够尝试枚举哪些位置的括号是可以删除的,因为不管是删除哪些位置的左括号或者是右括号对应的位置的数量相等那么结果就是一样的,我们应该规定一个顺序,固定删除的是多少个括号,例如"(((((())",我们可以尝试删除一个,两个...四个左括号而不是枚举哪些位置是可以删除的,枚举删除几个括号可以避免求解出重复的答案。

② 由①可以知道我们可以使用递归解决,递归的主要思路如下,主要是分为三种情况,第一种是既不是左括号也不是右括号那么往下递归即可,因为这些字符肯定是不可以删除的,第二种是当前递归的位置对应的字符是左括号,我们可以尝试从当前位置删除t,t - 1...0个左括号,对于右括号的情况也是类似的,我们需要在递归方法中传入如下的参数:a: 当前的括号序列s,b:当前递归的位置index,c:左括号减去右括号的数目count,当前可以删除的左括号的数目l,当前可以删除的右括号的数目r(l, r可以用来减枝控制删除的括号数目),记录递归过程的括号序列path,记录答案的res。对于当前字符是左括号的情况,我们先计算一下从当前位置连续的左括号数目,当前可删除的左括号数目减去这些连续的数目t,然后在循环中尝试删除t,t - 1...0,我们是在循环中删除括号的时候更新对应的参数path,l,第一次的时候如果往下递归表示在满足当前可删除左括号数目的情况下删除全部的左括号,这个时候往下递归即可,for循环的第二次的时候比上一次少删除一个括号,那么就需要更新path,表示当前删除这么多个左括号,剩下一个没有删除,...一直到删除0个括号,并且需要更新当前可删除左括号的数目l和左括号减去右括号的数目(所以一开始的时候直接减去可以连续的左括号数目然后在循环中更新对应的参数往下递归即可)。对于右括号的情况也是类似的。

3. 代码如下:

from typing import List

class Solution:
    # index表示当前递归的括号序列的位置, count表示左括号减去右括号的数目, l, r表示当前可以删除的左/右括号的数目, path记录下递归过程中可能合法的括号序列, res记录答案
    def dfs(self, s: str, index: int, count: int, l: int, r: int, path: str, res: List[int]):
        if index == len(s):
            if count == 0:
                res.append(path)
            return
        if s[index] != "(" and s[index] != ")":
            self.dfs(s, index + 1, count, l, r, path + s[index], res)
        elif s[index] == "(":
            k = index
            # 枚举有多少个连续的左括号"("
            while k < len(s) and s[k] == "(": k += 1
            l -= k - index
            # 尝试删除当前左括号的数目, 第一次尝试删除全部, 第二次比上一次少一个所以需要更新path, count, l的值, 表示对应删除完相应左括号剩下的括号序列, 当前左括号减去右括号的数目, 可删除的左括号数目
            for i in range(k - index, -1, -1):
                if l >= 0: self.dfs(s, k, count, l, r, path, res)
                l += 1
                path += "("
                count += 1
        elif s[index] == ")":
            k = index
            while k < len(s) and s[k] == ")": k += 1
            r -= k - index
            for i in range(k - index, -1, -1):
                if count >= 0 and r >= 0:
                    self.dfs(s, k, count, l, r, path, res)
                r += 1
                path += ")"
                count -= 1

    def removeInvalidParentheses(self, s: str) -> List[str]:
        # l, r记录需要删除的左右括号的数量可以用来减枝
        l, r = 0, 0
        for c in s:
            if c == "(":
                l += 1
            elif c == ")":
                if l == 0:
                    r += 1
                else:
                    l -= 1
        res = list()
        self.dfs(s, 0, 0, l, r, "", res)
        return res

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值