【Leetcode】301. Remove Invalid Parentheses

题目地址:

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

给定一个含左右括号的字符串 s s s(可能还含别的字符),要求删掉其若干括号使得括号序列是合法的。问最少删掉多少括号,返回删去括号之后的字符串的所有方案。

一个括号序列是合法的,当且仅当:
1、左括号右括号数量相等;
2、任意前缀中,左括号数量大于等于右括号数量。
那么我们可以先扫描一遍 s s s,算出至少要删除的左括号和右括号的数量。用lr记录左括号右括号个数。如果遇到左括号,则l++;如果遇到右括号,则看一下l == r是否成立,如果成立,说明当前遍历到的右括号一定要删掉,否则可以不删;当遍历结束之后,看一下l > r是否成立,如果成立,则要将多余的左括号删掉。所以扫描完之后就能知道要删除多少个左右括号了。更简单的办法是用栈模拟,直接就设lr是要删的左右括号个数,想象有一个栈,遇到左括号就入栈(即l++l是栈内左括号的个数),遇到右括号,如果栈里没有左括号,那么该右括号要删掉,从而r++;如果栈里有左括号,那么左括号出栈(即l--)。最后的l就是要删除的左括号个数,r是要删除的右括号个数。
接下来,就是DFS的过程了。在每个位置,如果当前位置是(,那么就枚举可以删除多少个左括号;如果当前位置是)则类似做;如果当前位置不是括号,那就直接append。每层递归的时候都要保证已经append的括号序列是合法前缀,并且左右括号删掉的个数没有超过上面算出来的个数。枚举到字符串末尾时如果得到了合法字符串就加入答案。代码如下:

class Solution {
 public:
  vector<string> removeInvalidParentheses(string s) {
    // 求一下要删除的左右括号的个数
    int l = 0, r = 0;
    for (int i = 0; i < s.size(); i++) {
      if (s[i] == '(')
        l++;
      else if (s[i] == ')') {
        if (!l)
          r++;
        else
          l--;
      }
    }

    vector<string> res;
    string t;
    dfs(0, s, t, 0, l, r, res);
    return res;
  }
  
  // u是指当前遍历到了s[u],t是当前生成的字符串,diff是t中左右括号的个数之差,l和r是还需要删除多少个左右括号
  void dfs(int u, string& s, string& t, int diff, int l, int r,
           vector<string>& res) {
    // 如果遍历到末尾了,那么得到了一个方案t,如果t满足合法括号条件,则加入答案。dfs的时候一定保证diff非负
    if (u == s.size()) {
      if (!diff) res.push_back(t);
      return;
    }

	// 如果当前字符不是括号,则必须加入答案
    if (s[u] != '(' && s[u] != ')') {
      t += s[u];
      dfs(u + 1, s, t, diff, l, r, res);
      t.pop_back();
      return;
    }
	
	// 否则说明是括号
    if (s[u] == '(') {
      // 数一下有多少个左括号
      int i = u;
      while (i < s.size() && s[i] == '(') i++;
      // 枚举删掉多少个左括号
      for (int k = i - u; k >= 0; k--) {
        // 如果还能删,就枚举之
        if (k <= l) dfs(i, s, t, diff + (i - u - k), l - k, r, res);
        t += '(';
      }
      // 恢复现场
      t.erase(t.end() - (i - u + 1), t.end());
    } else {
      int i = u;
      while (i < s.size() && s[i] == ')') i++;
      for (int k = i - u; k >= 0; k--) {
        // 这里有两个条件,删的右括号数不多于r,并且删掉之后仍然满足t中左括号数>=右括号数
        if (k <= r && diff - (i - u - k) >= 0)
          dfs(i, s, t, diff - (i - u - k), l, r - k, res);
        t += ')';
      }
      t.erase(t.end() - (i - u + 1), t.end());
    }
  }
};

时间复杂度 O ( n 2 n ) O(n2^n) O(n2n),空间 O ( n ) O(n) O(n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值