n이 주어지면 n쌍의 올바른 괄호형태의 모든 경우의수를 리턴하라.
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
괄호가 나열되는 모든 경우의 수 중에서 정상적인 괄호만 추출하기. parans 문자열로 next_permutation을 사용하려면 parans가 초기에 정렬된 상태여야 하기 때문에 "((()))"
가 되어야한다.
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ret;
std::string parans;
parans.append(n, '(');
parans.append(n, ')');
do {
if (is_well_formed(parans)) {
//cout << parans << " ";
ret.push_back(parans);
}
} while (std::next_permutation(parans.begin(), parans.end()));
return ret;
}
bool is_well_formed(string s) {
std::stack<char> stk;
for (char c: s) {
if (c == '(')
stk.push('(');
else if (c == ')')
if (!stk.empty())
stk.pop();
}
if (stk.empty())
return true;
else
return false;
}
};
backtracking 으로 더 효율적으로 해결. 좌우 괄호갯수가 각각 n이 될때까지 str 뒤에 괄호를 합쳐서 모든 경우의 수 탐색.
(이부분이 이 코드의 핵심일듯!)
if (nr_right < nr_left)
recur(ret, nr_left, nr_right + 1, str + ")", n);
오른쪽에 )
를 덧붙일수 있는 조건은, 덧 붙이기 전 항상 왼쪽의 (
갯수가 더 많아야한다. 반면 (
를 를 덧붙이기 위한 조건은 n개 이하 이기만 하면된다.
시간복잡도는 한 함수에서 재귀함수가 두번호출되었으니 O(2^N)이다.
#include <iostream>
#include <vector>
#include <string>
class Solution {
public:
std::vector<string> generateParenthesis(int n) {
std::vector<string> ret;
std::string str;
recur(ret, 0, 0, "", n);
return ret;
}
void recur(std::vector<string> &ret, int nr_left, int nr_right, std::string str, int n) {
/* base case */
if (nr_left == n && nr_right == n) {
ret.push_back(str);
return;
}
/* recursion */
if (nr_left < n)
recur(ret, nr_left + 1, nr_right, str + "(", n);
if (nr_right < nr_left)
/* if you want to append ")" on right side, it is required that number of "(" is less than ")" */
recur(ret, nr_left, nr_right + 1, str + ")", n);
}
};
(
를 append해서 전달하면 그 다음 recur()함수에서 이미 append되어버린 문자열을 가지고 전달을 한다. str 변수가 바뀌지 않도록 str + "(" 로 전달해야한다. 어이없는 실수! if (nr_left < n)
recur(ret, nr_left + 1, nr_right, str.append("("), n);
if (nr_right < nr_left)
recur(ret, nr_left, nr_right + 1, str.append(")"), n);