Leetcode: 删除无效的括号

33 阅读3分钟

原题:leetcode.cn/problems/re…

题目描述

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。

返回所有可能的结果。答案可以按 任意顺序 返回。

解题思路

  1. 最小删除量计算:遍历字符串,统计未匹配的左右括号数量 lremove/rremove(需删除的最少括号数)。
  2. 回溯搜索:从起始位置遍历字符串,递归尝试删除每个无效括号:
    • 跳过连续重复括号避免重复解
    • 剩余字符不足时剪枝
    • 删除左括号或右括号后更新对应删除量
  3. 结果验证:当删除量归零时,用栈验证括号有效性:
    • 遇左括号计数+1,遇右括号计数-1
    • 中途出现负值立即终止
    • 最终计数为零则为有效解

关键洞察

  1. 删除量确定性:最小删除量 (lremove,rremove) 是固定值,直接作为搜索边界。
  2. 重复括号优化:跳过连续相同括号(str[i] === str[i-1]),避免生成重复解。
  3. 双效剪枝
    • 资源剪枝:剩余字符数 < lremove+rremove 时终止
    • 逻辑剪枝:优先删除无效括号缩小搜索空间
  4. 延迟验证:仅在删除完成时检查有效性,减少中间状态验证开销。

复杂度分析

指标说明
时间复杂度O(n·2^n):最坏情况每个括号有删/留选择(n≤25 实际剪枝高效)
空间复杂度O(n²):递归深度 O(n),每层 substr 创建新字符串(O(n))
验证复杂度O(n):isValid 单次遍历,仅终点调用

:剪枝策略使实际性能远优于理论最坏复杂度,字符串约束(n≤25)保证可行性。

代码实现

  • 递归
    /**
     * @link {https://leetcode.cn/problems/remove-invalid-parentheses}
     * @param {string} s
     * @return {string[]}
     */
    var removeInvalidParentheses = function(s) {
        const helper = (str, start, lremove, rremove) => {
            if (lremove === 0 && rremove === 0) {
                if (isValid(str)) {
                    res.push(str);
                }
                return;
            }
            for (let i = start; i < str.length; i++) {
                if (i !== start && str[i] === str[i - 1]) {
                    continue;
                }
                // 如果剩余的字符无法满足去掉的数量要求,直接返回
                if (lremove + rremove > str.length - i) {
                    return;
                } 
                // 尝试去掉一个左括号
                if (lremove > 0 && str[i] === '(') {
                    helper(str.substr(0, i) + str.substr(i + 1), i, lremove - 1, rremove);
                }
                // 尝试去掉一个右括号
                if (rremove > 0 && str[i] === ')') {
                    helper(str.substr(0, i) + str.substr(i + 1), i, lremove, rremove - 1);
                }
            }
        }
        const res = [];
        let lremove = 0;
        let rremove = 0;
        for (const c of s) {
            if (c === '(') {
                lremove++;
            } else if (c === ')') {
                if (lremove === 0) {
                    rremove++;
                } else {
                    lremove--;
                }
            }
        }
        helper(s, 0, lremove, rremove);
        return res;
    }
    const isValid = (str) => {
        let cnt = 0;
    
        for (let i = 0; i < str.length; i++) {
            if (str[i] === '(') {
                cnt++;
            } else if (str[i] === ')') {
                cnt--;
                if (cnt < 0) {
                    return false;
                }
            }
        }
        return cnt === 0;
    }
    
  • 迭代
    const removeInvalidParentheses = (s) => {
        const isValid = (str) => {
            let balance = 0;
            for (const c of str) {
                if (c === '(') balance++;
                else if (c === ')') balance--;
                if (balance < 0) return false;
            }
            return balance === 0;
        };
        
        let queue = new Set([s]);
        while (queue.size) {
            const nextQueue = new Set();
            const validResults = [];
            
            for (const expr of queue) {
                if (isValid(expr)) {
                    validResults.push(expr);
                }
                // 找到有效表达式后停止生成下一层
                if (validResults.length) continue; 
                
                // 生成所有删除一个括号的表达式
                for (let i = 0; i < expr.length; i++) {
                    if (expr[i] !== '(' && expr[i] !== ')') continue;
                    const newExpr = expr.slice(0, i) + expr.slice(i + 1);
                    nextQueue.add(newExpr);
                }
            }
            
            if (validResults.length) return validResults;
            queue = nextQueue;
        }
        return [""];
    };
    

实际应用场景

  1. 公式编辑器: 自动修复数学公式中的括号不匹配`
  2. SQL解析器: 修复嵌套查询中的括号错误`
  3. 编译器前端: 处理代码中的括号不匹配错误`
  4. 交互式教程: 括号匹配的编程教学工具`

相似题目