Given n pairs of parentheses, write a function to generate all combinations of
well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1
Output: ["()"]
Constraints:
1 <= n <= 8
符合官方答案时间复杂度的代码如下所示:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
int open = 0, close = 0, max = n;
cases("", res, open, close, max, 2*n);
return res;
}
private:
void cases(string pref, vector<string> &res, int open, int close, int max, int left){
if(left == 0){
res.push_back(pref);
return;
}
if(open < max){
cases(pref + "(", res, open + 1, close, max, left - 1);
}
if(close < open){
cases(pref + ")", res, open, close + 1, max, left - 1);
}
}
};
起初的代码如下所示:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
vector<string> result;
cases("", res, 2*n);
for(auto r : res){
if(isok(r))
result.push_back(r);
}
return result;
}
private:
void cases(string pref, vector<string> &res, int left){
if(left == 0){
res.push_back(pref);
return;
}
cases(pref + "(", res, left - 1);
cases(pref + ")", res, left - 1);
}
bool isok(string s){
stack<char> sk;
for(int i = 0; i < s.size(); ++i){
if(!sk.empty() && hit(sk.top(), s[i])){
sk.pop();
}
else{
sk.push(s[i]);
}
}
return sk.empty();
}
bool hit(char a, char b){
if(a == '(' && b == ')')
return true;
return false;
}
};
感觉面试的过程中,如果写不出来最优解答。写出上述第二种方案,我感觉至少可以体现自己的基本功还算比较扎实,这里的基本功涉及到两方面:1是如何用代码实现数学中的组合;2是如何利用stack来对付“俄罗斯方块”或"接竹竿"问题。第一点的实现,注意将"前缀"不断的叠加传入,并将result也以引用的方式传入。第二点的实现,则是另外一道easy题目的简化。
如何想在面试中写出最优方案,其关键点在于找到一种更加快速的判断字符串是否valid的方式,在“组合”的过程中及时early stop。这种更加快速判断的方式就是:如果在字符串成长的过程中,open括号的数量始终小于等于n, 且close括号的数量始终小于等于open括号的数量,且最终open括号的数量等于close括号的数量。则为有效。这个是断言是valid的充分必要条件。我们可以根据该断言的必要性来及时early stop, 根据该断言的充分性做最终的方案收集。就可以形成上述最优 的代码。
此题的另外一个启发是,如果想对一个组合问题(多叉树)进行耗时优化,一种优化思路就是early stop。体现在代码上就是给递归出加上条件语句,如果条件语句不满足,就不会向更深处延伸,从而early stop.
if(open < max){
cases(pref + "(", res, open + 1, close, max, left - 1);
}
if(close < open){
cases(pref + ")", res, open, close + 1, max, left - 1);
}
这边还想提到的一点就是,官方的代码是先append, 再删除貌似没有我这样写简洁?
if (open < max) {
cur.append("(");
backtrack(ans, cur, open+1, close, max);
cur.deleteCharAt(cur.length() - 1);
}
if (close < open) {
cur.append(")");
backtrack(ans, cur, open, close+1, max);
cur.deleteCharAt(cur.length() - 1);
}