leetcode题解:第301题Remove Invalid Parentheses

【密码学】生成8位26个字母和数字的全排列(密码字典,密钥)c++代码(非递归高效直接) 生成8位26个字母和数字的全排列(密钥)代码(非递归高效直接) 用C语言或C++,生成一个8位的26个字母和数字的全排列的原代码,例如从:00000000到ZZZZZZZZ。 说明:生成的这个字典会非常大,有36^8条记录,估计要3T的硬盘,跑一个晚上看看         这个方法比递归更高效,更直接。                                                ... 阅读详情

https://leetcode-cn.com/problems/remove-invalid-parentheses/

解法一、BFS

这题要求所有的解,所以我们应当想到用搜索来解决,BFS的思想会比较直观:

  1. 由题目要求易知:所有的解都是由原始字符串删除了相同数量的括号得到的
  2. 对于删除的括号数量i,生成对应的所有的字符串,它们属于第i个level
  3. 对于每个level,判断其中的字符串是否有解:若有,则所有的解都来自于这个level;否则,进入到下一个level来寻找解

注意: 容易发现,每个level会有很多重复的字符串,所以我们需要用集合来进行去重。

如何判断一个字符串合法?可以通过左右括号的数量来判断。遍历过程中右括号的数量必须小于等于左括号的数量,最终左右括号数量必须相等,这才是一个合法的字符串。

代码
class Solution {
    public List<String> removeInvalidParentheses(String s) {
        List<String> ans = new LinkedList<String>();
        Queue<String> level = new LinkedList<String>();
        Set<String> visited = new HashSet<String>();
        level.offer(s);
        while (!level.isEmpty() && ans.isEmpty()) {
            int size = level.size();
            visited.clear();
            for (int i = 0; i < size; ++i) {
                s = level.poll();
                if (isValid(s)) {
                    ans.add(s);
                    continue;
                }
                for (int k = 0; k < s.length(); ++k) {
                    if (s.charAt(k) == '(' || s.charAt(k) == ')') {
                        String str = s.substring(0, k) + s.substring(k + 1);
                        if (!visited.contains(str)) {
                            visited.add(str);
                            level.offer(str);
                        }
                    }
                }
            }
        }
        return ans;
    }

    private boolean isValid(String s) {
        int left = 0, right = 0;
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            if (c == '(') left++;
            else if (c == ')') right++;
            if (left < right) return false;
        }
        return left == right;
    }
}

解法二、回溯

BFS的解法虽然简单,但是时间复杂度却比较高,因为我们对一个level中的所有字符串都进行了一遍遍历来判断是否合法,生成可能的字符串的过程也不够优雅。
DFS(回溯)的想法十分巧妙,它不以删除括号为目标,而是通过递归去遍历原始字符串,在递归过程中维护一个expression,最终的expression就是一个可能的解,需要判断其是否合法。 递归过程的细节:

  1. 对每一个字符c做两种选择:是否加入到expression中
  2. 维护加入到expression的左括号和右括号数量,在递归过程中,只有rightCount <= leftCount的expression才可能是一个合法的字符串,如果最终leftCount == rightCount,则expression是一个合法的字符串
  3. 维护已经删除的括号数量removed,由于题目要求删除最少数量的括号,所以对每一个合法的expression,还必须保证removed是最小的
  4. 这样生成的expression也是有重复的,所以同样需要用集合来进行去重
代码
class Solution {
    private Set<String> ans = new HashSet<String>();
    private int minRemoved = Integer.MAX_VALUE;

    public List<String> removeInvalidParentheses(String s) {
        backtrack(s, 0, 0, 0, new StringBuilder(), 0);
        return new ArrayList<String>(this.ans);
    }

    private void backtrack(String s, int index, int leftCount, int rightCount, StringBuilder expression, int removed) {
        if (index == s.length()) {
            if (leftCount == rightCount && removed <= minRemoved) {
                if (removed < minRemoved) {
                    ans.clear();
                    this.minRemoved = removed;
                }
                this.ans.add(expression.toString());
            }
        }
        else {
            char c = s.charAt(index);
            int length = expression.length();
            if (c != '(' && c != ')') {
                expression.append(c);
                backtrack(s, index + 1, leftCount, rightCount, expression, removed);
                expression.deleteCharAt(length);
            }
            // 注意这个else里会前后执行两次backtrack,
            // 前一次backtrack对expression造成的修改不应该影响第二次backtrack
            // 所以需要回溯,删去对expression的修改
            else {
                // 删掉这个括号
                backtrack(s, index + 1, leftCount, rightCount, expression, removed + 1);
                // 不删掉,即加入expression
                expression.append(c);
                if (c == '(') backtrack(s, index + 1, leftCount + 1, rightCount, expression, removed);
                else if (rightCount < leftCount) backtrack(s, index + 1, leftCount, rightCount + 1, expression, removed);
                expression.deleteCharAt(length);
            }
        }
    }
}

解法三、有限制的回溯

解法二的思想虽然巧妙地减少了判断字符串是否合法的时间,但由于会生成非常多的expression,时间复杂度同样较高(BFS则不会生成太多,若在某一个level找到解,则停止进入下一个level)。
为什么解法二会生成非常多的expression?因为我们没有对删除的括号数量进行限制,而是先删除,再比较删除数量的大小,这样自然就会导致很多生成的expression其对应的删除数量很大,并不是我们想要的解。简单来说,就是没有充分剪枝。
如果我们能从原始字符串中找出需要删除的左右括号数量leftRemrightRem,那么在回溯过程中就能通过限制删除括号的数量,进行很多剪枝,大量地减少递归的次数。
我们可以通过一次遍历来找到leftRemrightRem

  1. 如果c == '(',则leftRem++
  2. 如果c == ')',则需要判断它是否有对应的(,如果有,则是合法的,如果没有,则是非法的。具体地来说,如果leftRem == 0,则rightRem++;否则leftRem--
  3. 最终的leftRemrightRem就是我们想要的结果
代码
class Solution {
    private Set<String> ans = new HashSet<String>();

    public List<String> removeInvalidParentheses(String s) {
        // 统计应该删除多少左括号和右括号
        int leftRem = 0, rightRem = 0;
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            if (c == '(') leftRem++;
            else if (c == ')') {
                if (leftRem == 0) rightRem++;
                else leftRem--;
            }
        }
        backtrack(s, 0, 0, 0, leftRem, rightRem, new StringBuilder());
        return new ArrayList(this.ans);
    }

    private void backtrack(String s, int index, int leftCount, int rightCount, int leftRem, int rightRem, StringBuilder expression) {
        if (index == s.length()) {
            if (leftCount == rightCount) this.ans.add(expression.toString());
        }
        else {
            char c = s.charAt(index);
            int length = expression.length();
            if (c != '(' && c != ')') {
                expression.append(c);
                backtrack(s, index + 1, leftCount, rightCount, leftRem, rightRem, expression);
                expression.deleteCharAt(length);
            }
            // 注意这个else里会前后执行两次backtrack,
            // 前一次backtrack对expression造成的修改不应该影响第二次backtrack
            // 所以需要回溯,删去对expression的修改
            else {
                // 删掉这个括号
                if (c == '(' && leftRem > 0) backtrack(s, index + 1, leftCount, rightCount, leftRem - 1, rightRem, expression);
                else if (rightRem > 0) backtrack(s, index + 1, leftCount, rightCount, leftRem, rightRem - 1, expression);
                // 不删掉,即加入expression
                expression.append(c);
                if (c == '(') backtrack(s, index + 1, leftCount + 1, rightCount, leftRem, rightRem, expression);
                else if (rightCount < leftCount) backtrack(s, index + 1, leftCount, rightCount + 1, leftRem, rightRem, expression);
                expression.deleteCharAt(length);
            }
        }
    }
}
【100%通过率】华为od统一考试B卷【需要打开多少监视器】JAVA&Go 实现 某长方形停车场,每个车位上方都有对应监控器,当且仅当在当前车位或者前后左右四个方向任意一个车位范围停车时,监控器才需要打开:给出某一时刻停车场的停车分布,请统计最少需要打开多少个监控器 阅读详情

相关推荐

数学物理方程大作业(有限弦振动特性研究_附程序MATLAB)

PDE案例学习数学物理方程大作业(有限弦振动特性研究_附程序MATLAB)

LeetCode每日一】——301.删除无效的括号

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

IronmanJay 1539

Java实现 LeetCode 301 删除无效的括号

301. 删除无效的括号 删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果。 说明: 输入可能包含了除 ( 和 ) 以外的字符。 示例 1: 输入: “()())()” 输出: [“()()()”, “(())()”] 示例 2: 输入: “(a)())()” 输出: [“(a)()()”, “(a())()”] 示例 3: 输入: “)(” 输出: [“”] class Solution { public List removeInvalidParentheses(String s) { int left = 0, right = 0;

Remove Invalid Parentheses

目来源 leetcode 301 给定一个包含许多左括号和右括号的字符串,删除最少的括号,使得删除括号后的字符串中的括号匹配,要求输出所有结果。 首先来计算一下究竟最少要删除多少个左括号和右括号。可以从左到右扫描字符串,用x1和x2分别记录要删除的左括号和右括号,遇到左括号时x1+1,遇到右括号时若x1大于0,则x1-1,否则x2+1,这样可以保证删除最少的左括号和右括号。 之后对整个字符

Alliyou的博客 550

Leetcode301:删除无效的括号

1.目描述 给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。 返回所有可能的结果。答案可以按任意顺序返回。 示例1: 输入:s = "()())()" 输出:["(())()","()()()"] 示例2: 输入:s = ")(" 输出:[""] 链接:https://leetcode-cn.com/problems/remove-invalid-parentheses/ 来源:力扣(LeetCode) 2.思路分析 这道...

qq_39144436的博客 772

【递归 &回溯】LeetCode-301. 删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按 任意顺序 返回。时间复杂度:O(2^n * n)满足有效括号序列的性质。空间复杂度:O(n)

xiaoxiawancsdn的博客 669

LeetCode301 删除无效的括号

LeetCode301

sakuraSD的博客 278

[LeetCode] 301. Remove Invalid Parentheses

:https://leetcode.com/problems/short-encoding-of-words/ 目大意 参考目 思路 set 集合 将所有word 放入set中,然后遍历所有set中的word,将word的从头的子串都从set中删除,最后统计 set中所有(word 的长度 + 1)(’#’) class Solution { public int minimumLe...

u013383813的博客 157

LeetCode 301. Remove Invalid Parentheses

Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note:The input string may contain letters other than the parentheses(and). ...

taoqick的专栏 796

[Leetcode] 301. Remove Invalid Parentheses

Problems: https://leetcode.com/problems/remove-invalid-parentheses/ Solution: class Solution { public List<String> removeInvalidParentheses(String s) { char[] arr = s.toCharArray(); ...

Renoler的博客 123

LeetCode-301. Remove Invalid Parentheses

Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note:The input string may contain letters other than the parentheses(and). ...

reigns的博客 308

LeetCode 301. Remove Invalid Parentheses【BFS求最小次数】⭐⭐⭐⭐⭐

不剪枝会超时 硬币找零 最小值的时候 文章目录目描述知识点结果实现码前思考代码实现码后反思 目描述 知识点 BFS+最小次数 结果 实现 码前思考 看到这道目我完全不知道该怎么写,即使我之前已经写过了一道关于 BFS求最小次数 的目了。。。 困扰我的可能就是怎么判断括号合法这个问,我一直想着用栈来判断,但是我看了别人的题解,才发现原来可以直接通过count计数左右括弧来判断匹配情况,而且还可以提前终止! 代码实现 //类似于那个最小和的情况,我们使用层次代表最小的删除 //此处有一个

yc_cy1999的博客 307

301. Remove Invalid Parentheses删除无效的括号

艾恩凝 个人博客https://aeneag.xyz/ 公众号 技术乱舞 今天的自己感觉有点难 2021/10/15 目 https://leetcode-cn.com/problems/remove-invalid-parentheses/ 目分析 又是作废的一天,BFS,好想法,参考网上大佬的,为什么这么菜 删除字符串的一个节点,看看是否可以 题解 class Solution { public: bool Is_Valid(const string&...

艾恩凝 183

301. Remove Invalid Parentheses

目: Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note: The input string may contain letters other than the parentheses ( and...

weixin_34319817的博客 99

LeetCode301——删除无效的括号-BFS解决

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按 任意顺序 返回。输出:[“(a())()”,“(a)()()”]输出:[“(())()”,“()()()”]输入:s = “(a)())()”来源:LeetCode301。输入:s = “()())()”输入:s = “)(”

weixin_52122642的博客 373

LeetCode 301. 删除无效的括号【字符串,回溯或BFS】困难

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分目有锁,本系列将至少持续到刷完所有无锁之日为止;由于LeetCode还在不断地创建新,本系列的终止日期可能是永远。在这一系列刷文章中,我不仅会讲解多种解思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。。

memcpy0的博客 661

LeetCode 301. 删除(最少)无效的括号(Hard)

目链接 题解 BFS简单而又详细的python讲解 思路 代码 class Solution: ### 0124 BFS(156 ms,15.2 MB) def removeInvalidParentheses(self, s: str) -> List[str]: # 判断当前字符串是否合法 def isValid(s): cnt = 0 for c in s: .

newCraftsman的博客 243

leetcode301周赛

现有一台饮水机,可以制备冷水、温水和热水。每秒钟,可以装满2杯类型的水或者1杯任意类型的水。给你一个下标从开始、长度为3的整数数组amount,其中amount[0]amount[1]和amount[2]分别表示需要装满冷水、温水和热水的杯子数量。返回装满所有杯子所需的秒数。

m0_53201842的博客 855
上一篇: leetcode题解:第399题Evaluate Division
下一篇: 详解leetcode石头合并问题
chenf99
博客等级 码龄10年 44粉丝 83原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值