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