Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.html
Note: The input string may contain letters other than the parentheses (
and )
.java
Example 1:数组
Input: "()())()" Output: ["()()()", "(())()"]
Example 2:函数
Input: "(a)())()" Output: ["(a)()()", "(a())()"]
Example 3:post
Input: ")(" Output: [""]
Credits:
Special thanks to @hpplayer for adding this problem and creating all test cases.this
Subscribe to see which companies asked this questionurl
解法一:spa
class Solution { public: vector<string> removeInvalidParentheses(string s) { vector<string> res; unordered_set<string> visited{{s}}; queue<string> q{{s}}; bool found = false; while (!q.empty()) { string t = q.front(); q.pop(); if (isValid(t)) { res.push_back(t); found = true; } if (found) continue; for (int i = 0; i < t.size(); ++i) { if (t[i] != '(' && t[i] != ')') continue; string str = t.substr(0, i) + t.substr(i + 1); if (!visited.count(str)) { q.push(str); visited.insert(str); } } } return res; } bool isValid(string t) { int cnt = 0; for (int i = 0; i < t.size(); ++i) { if (t[i] == '(') ++cnt; else if (t[i] == ')' && --cnt < 0) return false; } return cnt == 0; } };
下面来看一种递归解法,这种解法首先统计了多余的半括号的数量,用cnt1表示多余的左括号,cnt2表示多余的右括号,由于给定字符串左右括号要么同样多,要么左括号多,要么右括号多,也可能左右括号都多,好比")("。因此cnt1和cnt2要么都为0,要么都大于0,要么一个为0,另外一个大于0。好,下面进入咱们的递归函数,首先判断,若是当cnt1和cnt2都为0时,说明此时左右括号个数相等了,咱们调用isValid子函数来判断是否正确,正确的话加入结果res中并返回便可。不然从start开始遍历,这里的变量start表示当前递归开始的位置,咱们不须要每次都从头开始,会有大量重复计算。并且对于多个相同的半括号在一块儿,咱们只删除第一个,好比"())",这里有两个右括号,咱们无论删第一个仍是删第二个右括号都会获得"()",没有区别,因此只用算一次就好了,咱们经过和上一个字符比较,若是不相同,说明是第一个右括号,若是相同则直接跳过。此时来看若是cnt1大于0,说明此时左括号多,而若是当前字符正好是左括号的时候,咱们能够删掉当前左括号,继续调用递归,此时cnt1的值就应该减1,由于已经删掉了一个左括号。同理,若是cnt2大于0,说明此时右括号多,而若是当前字符正好是右括号的时候,咱们能够删掉当前右括号,继续调用递归,此时cnt2的值就应该减1,由于已经删掉了一个右括号。参见代码以下:code
解法二:htm
class Solution { public: vector<string> removeInvalidParentheses(string s) { vector<string> res; int cnt1 = 0, cnt2 = 0; for (char c : s) { cnt1 += (c == '('); if (cnt1 == 0) cnt2 += (c == ')'); else cnt1 -= (c == ')'); } helper(s, 0, cnt1, cnt2, res); return res; } void helper(string s, int start, int cnt1, int cnt2, vector<string>& res) { if (cnt1 == 0 && cnt2 == 0) { if (isValid(s)) res.push_back(s); return; } for (int i = start; i < s.size(); ++i) { if (i != start && s[i] == s[i - 1]) continue; if (cnt1 > 0 && s[i] == '(') { helper(s.substr(0, i) + s.substr(i + 1), i, cnt1 - 1, cnt2, res); } if (cnt2 > 0 && s[i] == ')') { helper(s.substr(0, i) + s.substr(i + 1), i, cnt1, cnt2 - 1, res); } } } bool isValid(string t) { int cnt = 0; for (int i = 0; i < t.size(); ++i) { if (t[i] == '(') ++cnt; else if (t[i] == ')' && --cnt < 0) return false; } return cnt == 0; } };
下面这种解法是论坛上的高票解法,思路确实很巧妙。递归函数的参数中,last_i表示当前遍历到的位置,至关上面解法中的start,last_j表示上一个删除的位置,这样能够避免重复计算。而后有个括号字符数组,初始化时放入左括号和右括号,博主认为这个字符数组是此解法最精髓的地方,由于其顺序能够改变,能够变成反向括号,这个就比较叼了,后面再讲它到底有多叼吧。咱们在递归函数中,从last_i开始遍历,在找正向括号的时候,用变量cnt表示括号数组中的左括号出现的次数,遇到左括号自增1,遇到右括号自减1。当左括号大于等于右括号的时候,咱们直接跳过。这个循环的目的是要删除多余的右括号,因此当cnt小于0的时候,咱们从上一个删除位置last_j开始遍历,若是当前是右括号,且是第一个右括号(关于这块能够参见上面解法中的分析),咱们删除当前右括号,并调用递归函数。注意这个for循环结束后要直接返回,由于进这个for循环的都是右括号多的,删到最后最可能是删成和左括号同样多,不须要再去翻转删左括号。好,最后来讲这个最叼的翻转,当字符串的左括号个数大于等于右括号的时候,不会进入第二个for循环,天然也不会return。那么因为左括号的个数可能会要大于右括号,因此咱们还要删除多余的左括号,因此咱们将字符串反转一下,好比"(()",反转变成")((",此时虽然咱们仍是要删除多余的左括号,可是反转后就没有合法的括号了,因此变成了找反向括号")(",那么仍是能够删除多余的左括号,而后咱们判断此时括号数组的状态,若是是正向括号,说明此时正要删除左括号,那么就调用递归函数,last_i和last_j均重置为0,括号数组初始化为反向括号。若是此时已是反向括号了,说明以前的左括号已经删掉了变成了")(",而后又反转了一下,变回来了"()",那么就能够直接加入结果res了,参见代码以下:
解法三:
class Solution { public: vector<string> removeInvalidParentheses(string s) { vector<string> res; helper(s, 0, 0, {'(', ')'}, res); return res; } void helper(string s, int last_i, int last_j, vector<char> p, vector<string>& res) { int cnt = 0; for (int i = last_i; i < s.size(); ++i) { if (s[i] == p[0]) ++cnt; else if (s[i] == p[1]) --cnt; if (cnt >= 0) continue; for (int j = last_j; j <= i; ++j) { if (s[j] == p[1] && (j == last_j || s[j] != s[j - 1])) { helper(s.substr(0, j) + s.substr(j + 1), i, j, p, res); } } return; } string rev = string(s.rbegin(), s.rend()); if (p[0] == '(') helper(rev, 0, 0, {')', '('}, res); else res.push_back(rev); } };
相似题目:
Different Ways to Add Parentheses
参考资料:
https://leetcode.com/problems/remove-invalid-parentheses/
https://leetcode.com/problems/remove-invalid-parentheses/discuss/75032/share-my-java-bfs-solution