LeetCode HOT100 - 删除无效的括号

首先是括号序列是否合法的判断

一个经典的判断形式是从左到右计数,( 为 1, ) 为 -1 ,中途如果小于 0 了,那么不合法,最后如果是 0 ,那么合法,否则也不合法

接着回到这一题

删除最小数量的无效括号,使得输入的字符串有效

1 <= s.length <= 25 长度并不大,可以考虑搜索

搜索的话要么是深度优先,要么是广度优先

从最小值的角度来说,广度优先自然一些

BFS 就是一层一层,第几层相当于删几个括号,这样如果某一层搜到了合法的字符,我们就不用继续扩展下一层了

bfs 的写法就是要注意我们是用队列存的,表示的是要操作的
但是每次我们是对一层进行操作,为了知道队列中哪些是这一层的,我们要记录层的大小来操作

class Solution {
public:
    vector<string> removeInvalidParentheses(string s) {
        auto check = [&](string t) -> int {
            int cnt = 0;
            for (auto i : t) {
                if (i != '(' && i != ')') {
                    continue;
                }
                cnt += i == '(' ? 1 : -1;
                if (cnt < 0) {
                    return 0;
                }
            }
            return cnt == 0;
        };
        queue<string> q;
        vector<string> ans;
        unordered_set<string> vis;
        q.push(s);
        vis.insert(s);
        int k = 0;
        while (!q.empty()) {
            int sz = q.size();
            for (int i = 0; i < sz; i++) {
                string t = q.front();
                q.pop();
                if (check(t)) {
                    k = 1;
                    ans.emplace_back(t);
                }
                if (k) {
                    continue;
                }
                for (int j = 0; j < t.size(); j++) {
                    if (t[j] != '(' && t[j] != ')') {
                        continue;
                    }
                    string tmp = t.substr(0, j) + t.substr(j + 1);
                    if (!vis.count(tmp)) {
                        q.push(tmp);
                        vis.insert(tmp);
                    }
                }
            }
            if (k) {
                break;
            }
        }
        return ans;
    }
};

再就是 dfs ,也是搜索状态,不过 dfs 一般就是要提前维护一些信息了,否则我们不知道成功的那个是不是最优

但实际上,我们要删除多少是可以提早知道的,同样从我们那个判断合法性的函数出发

从左到右扫描:

  • 遇到 (lremove++
  • 遇到 )
    • 如果此时 lremove > 0,说明它可以和前面的左括号匹配,于是 lremove--
    • 否则说明这个 ) 多余,rremove++
      最后:
  • lremove 表示多余的左括号数
  • rremove 表示多余的右括号数

dfs 中,我们就是根据这两个参数,来判断这个括号能否删除

当然,dfs 的核心是剪枝,不然其状态数会很多,也就是到了什么程度之后,我们没必要继续往深的搜索了

  • 比如字符串里有 "(((",删第一个和删第二个,结果其实一样。
    所以如果当前位置字符和前一个一样,就跳过,避免重复。

  • 如果当前剩余字符数都不足以删完 lremove + rremove,那这条路不可能成功,直接返回。

  • 合法性检查,已经合法了,当然不用继续了,更深层一定不优

class Solution {
public:
    vector<string> removeInvalidParentheses(string s) {
        auto check = [&](string t) -> int {
            int cnt = 0;
            for (auto i : t) {
                if (i != '(' && i != ')') {
                    continue;
                }
                cnt += i == '(' ? 1 : -1;
                if (cnt < 0) {
                    return 0;
                }
            }
            return cnt == 0;
        };
        int l = 0, r = 0;
        for (auto i : s) {
            if (i == '(') {
                l++;
            } else if (i == ')') {
                if (l) {
                    l--;
                } else {
                    r++;
                }
            }
        }
        vector<string> ans;
        unordered_set<string> st;
        auto dfs = [&](auto self, string t, int u, int l, int r) -> void {
            if (l == 0 && r == 0) {
                if (check(t) && !st.count(t)) {
                    ans.emplace_back(t);
                    st.insert(t);
                }
                return;
            }
            for (int i = u; i < t.size(); i++) {
                if (i > u && t[i] == t[i - 1]) {
                    continue;
                }
                if (l + r > t.size() - i) {
                    return;
                }
                if (l && t[i] == '(') {
                    self(self, t.substr(0, i) + t.substr(i + 1), i, l - 1, r);
                }
                if (r && t[i] == ')') {
                    self(self, t.substr(0, i) + t.substr(i + 1), i, l, r - 1);
                }
            }
        };
        dfs(dfs, s, 0, l, r);
        return ans;
    }
};
posted @ 2026-04-01 23:42  rdcamelot  阅读(10)  评论(0)    收藏  举报