这道题我们要明确几点:
- 删除最少的括号让string变valid,我们可能只删除左括号,()(;只删除右括号, ());或者两个都删除,例如)(。所以我们需要先从左往右扫一遍删除不valid的左括号之后再反向扫一遍删除右括号。
- 去重。重复的来源是连续的左括号和右括号,例如()))和(((),如果我们如果我们不限定删除哪两个,那么就会产生重复。所以我们对于这种情况的策略是,永远从连续区间的第一个开始删。
- 删除哪些括号。对于()()()),我们可以分别删除第1,2,3个右括号从而得到(()()), ()(()),()()()。显然当我们从左向右扫遇到第一个invalid的右括号时,也就是没有足够的左括号可以match它,这代表我们必须要删除一个右括号来使得序列合法。我们可以从头开始分别将每一个不产生重复的右括号删除(这样不会有问题,因为之前的每一个左括号还是有对应的右括号match),继续递归。并且记录这一次删除的位置,下一层递归要删除的话继续从这之后开始。
实现的时候,对于从左向右和从又向左的两个扫描,我们可以共用一套代码,第二次从右向左扫描的时候,我们把string做一个镜像即可。假设string长度为n,空间复杂度worst case O(n),递归深度。时间复杂度O(n),从左到右两个指针最多走一遍,再从右到左一次,代码如下:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Solution { | |
public: | |
vector<string> removeInvalidParentheses(string s) { | |
vector<string> res; | |
backtracking(res, s, 0, 0, false); | |
return res; | |
} | |
private: | |
void backtracking(vector<string>& res, string s, int start, int lastD, bool rev) | |
{ | |
int count = 0; | |
for(int i = start; i < s.size(); ++i) | |
{ | |
if(s[i] == '(')++count; | |
if(s[i] == ')')--count; | |
if(count < 0) | |
{ | |
for(int j = lastD; j <= i; ++j) | |
{ | |
if(s[j] == ')' && (!j || s[j - 1] != ')')) | |
backtracking(res, s.substr(0, j) + s.substr(j + 1), i, j, rev); | |
} | |
return; | |
} | |
} | |
if(!rev) | |
backtracking(res, mirror(s), 0, 0, true); | |
else | |
res.push_back(mirror(s)); | |
} | |
string mirror(string s) | |
{ | |
string res; | |
for(int i = s.size() - 1; i >= 0; --i) | |
{ | |
res += s[i] == ')'? '(': (s[i] == '('? ')': s[i]); | |
} | |
return res; | |
} | |
}; |
这道题和POJ 1141也是类似的。1141是求添加多少个括号可以使得序列合法并且只要输出一个合法串。这道题是删除,当然这道题的输入还包含了字母,这个比较好解决,输出的时候我们要输出所有的结果,这代表我们记录路径的时候要记录所有的最优分割点,之后我们递归地生成所有结果即可。DP解法比较麻烦的一点是没有比较好的去重的方法,这里我们用unordered_set来去重。除此之外,我们可以看到DP解法的时间空间复杂度也比DFS要差,需要O(n^2),所以还是DFS解法更好。代码如下:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Solution { | |
public: | |
vector<string> removeInvalidParentheses(string s) { | |
int len = s.size(); | |
//dp[i][j] represents minimum cost to make substring s[i, j] to be palindrome | |
//dp[i][j] = dp[i + 1][j], if s[i] is alpha | |
//dp[i][j] = dp[i + 1][j] + 1, if s[i] == ')' | |
//else, dp[i][j] = min(dp[i + 1][k - 1] + dp[k + 1][j]), i <= k <= j and s[k] == ')' | |
//use path to record the cut point for every [i, j] | |
//we are using vector<int> because there may be ties and [i, j] can be broken at multiple points | |
vector<vector<int>> dp(len + 1, vector<int>(len + 1, 0)); | |
unordered_map<int, vector<int>> path; | |
for(int i = 0; i < len; ++i)if(!isalpha(s[i]))dp[i][i] = 1; | |
for(int j = 0; j < len; ++j) | |
{ | |
for(int i = j - 1; i >= 0; --i) | |
{ | |
int key = i * len + j; | |
if(isalpha(s[i])) | |
{ | |
dp[i][j] = dp[i + 1][j]; | |
path[key].push_back(-1); | |
continue; | |
} | |
//assume s[i] doesn't match | |
dp[i][j] = dp[i + 1][j] + 1; | |
path[key].push_back(-1); | |
for(int k = i + 1; k <= j; ++k) | |
{ | |
if(s[i] == '(' && s[k] == ')') | |
{ | |
int cost = dp[i + 1][k - 1] + dp[k + 1][j]; | |
if(cost < dp[i][j]) | |
{ | |
dp[i][j] = cost; | |
path[key].clear(); | |
path[key].push_back(k);//s[k] matches s[i] | |
} | |
else if(cost == dp[i][j]) | |
path[key].push_back(k); | |
} | |
} | |
} | |
} | |
auto set = generate(s, 0, len - 1, len, path); | |
vector<string> res; | |
for(auto& str : set)res.push_back(str); | |
return res; | |
} | |
private: | |
unordered_set<string> generate(string& s, int i, int j, int len, unordered_map<int, vector<int>>& path) | |
{ | |
//follow the cut point and generate results recursively | |
if(i > j)return {""}; | |
if(i == j) | |
{ | |
if(isalpha(s[i]))return {string(1, s[i])}; | |
else return {""}; | |
} | |
auto cutPoints = path[i * len + j]; | |
unordered_set<string> res; | |
int size = cutPoints.size(); | |
for(int n = 0; n < size; ++n) | |
{ | |
int k = cutPoints[n]; | |
if(k == -1) | |
{ | |
for(auto str : generate(s, i + 1, j, len, path)) | |
res.insert(isalpha(s[i])? s[i] + str: str); | |
} | |
else | |
{ | |
unordered_set<string> lefts = generate(s, i + 1, k - 1, len, path); | |
unordered_set<string> rights = generate(s, k + 1, j, len, path); | |
for(auto& left : lefts) | |
for(auto& right : rights) | |
res.insert(s[i] == '('? "(" + left + ")" + right: left + right); | |
} | |
} | |
return res; | |
} | |
}; |
No comments:
Post a Comment