刪除最小數量的無效括號,使得輸入的字符串有效,返回所有可能的結果。
說明: 輸入可能包含了除 (
和 )
以外的字符。
示例 1:
輸入: "()())()"
輸出: ["()()()", "(())()"]
示例 2:
輸入: "(a)())()"
輸出: ["(a)()()", "(a())()"]
示例 3:
輸入: ")("
輸出: [""]
這道題翻譯了discuss的最高票解答(能看懂答案就不錯了)
這道題的基本思想是找到多餘的括號(注意是所有的),所以基本思路是採用回溯法找到所有有效解,然後加入答案中,先來看幾點啓發:
1:怎麼找到多餘的括號,在Valid Parentheses中,通過stack或者兩個計數器left和right分別記錄左括號和右括號的個數,可以驗證一個左右括號是否符合要求。
2:如何限制在指定序列中的有效括號數,比如str=()()),採用計數方法(left和right計數器)來統計,發現left=2,right=3,由於left-right>=0,所以當前的子字符串多了一個右括號,需要刪除一個),我們可以刪除str[1],形成str=(()),也可以刪除str[3],形成str=()(),爲了避免重複出現答案,這裏規定只刪除第一個不符合要求的左括號,因爲第二種情況會在後面的回溯中遍歷到)。
我們規定幾個變量的意思:start_to_count開始計數左右括號的位置,start_to_remove開始準備刪除第一個不符合要求右括號的起始搜索點(start_to_remove<=start_to_count)
class Solution {
public:
void Core(string s,vector<string> & res,int start_to_count,int start_to_remove,vector<char> &par){
int count=0; //採用計數器來統計左右括號數
for(int i=start_to_count;i<s.size();i++){
if(s[i]==par[0]){
count++; //遇到左括號就+1
}
if(s[i]==par[1]){
count--; //遇到右括號就-1
}
if(count>=0) continue; 如果左括號個數>=右括號個數,繼續尋找
for(int j=start_to_remove;j<=i;j++){ //能進到for循環表示count小於0,表示一定要刪除一個右括號,那麼從start_to_remove開始尋找,找到第一個不滿足要求的右括號的位置。
if(s[j]==par[1] && (j==start_to_remove || s[j-1]!=par[1])){ //第一個多餘的右括號的下標是s[j]==par[1],但是也要考慮邊界條件:比如j是第一個元素這種情況
Core(s.substr(0,j)+s.substr(j+1),res,i,j,par); //那麼刪除第一個右括號後的有效字符串就是把第j個字符刪掉的剩下的字符串,即new_str=s.substr(0,j)+s.substr(j+1),由於前面i個(下標)字符已經是有效的字符,且把第j個字符給刪了,還要考慮新字符串new_str的長度少了1,那麼新的開始搜索的字符下標new_start_to_count=i+1-1=i,同理new_start_to_remove=j+1-1,所以遞歸調用自身,對應參數設置如上所示。
}
}
return; //return這裏已經處理了剩下子串的所有情況,直接返回,不然會有重複數據。
}
reverse(s.begin(),s.end()); //反轉原字符,從右往左搜索刪除多餘的左字符
if(par[0]=='('){
vector<char> tmp={')','('};
Core(s,res,0,0,tmp);
}
else res.push_back(s);
}
vector<string> removeInvalidParentheses(string s) {
vector<char> par={'(',')'};
vector<string> res;
Core(s,res,0,0,par);
return res;
}
};