力扣22. 括号生成(动态规划)
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
动态规划
反思:
首先,面向小白:什么是动态规划?在此题中,动态规划的思想类似于数学归纳法,当知道所有 i<n 的情况时,我们可以通过某种算法算出 i=n 的情况。
本题最核心的思想是,考虑 i=n 时相比 n-1 组括号增加的那一组括号的位置。
思路:
当我们清楚所有 i<n 时括号的可能生成排列后,对与 i=n 的情况,我们考虑整个括号排列中最左边的括号。
它一定是一个左括号,那么它可以和它对应的右括号组成一组完整的括号 "( )",我们认为这一组是相比 n-1 增加进来的括号。
那么,剩下 n-1 组括号有可能在哪呢?
【这里是重点,请着重理解】
剩下的括号要么在这一组新增的括号内部,要么在这一组新增括号的外部(右侧)。
既然知道了 i<n 的情况,那我们就可以对所有情况进行遍历:
"(" + 【i=p时所有括号的排列组合】 + ")" + 【i=q时所有括号的排列组合】
其中 p + q = n-1,且 p q 均为非负整数。
事实上,当上述 p 从 0 取到 n-1,q 从 n-1 取到 0 后,所有情况就遍历完了。
注:上述遍历是没有重复情况出现的,即当 (p1,q1)≠(p2,q2) 时,按上述方式取的括号组合一定不同。
第一个循环i是动态规划向下求解
第二个循环j是得到dp[i]之前(dp[0]----dp[i-1])的全部组合情况,作为索引
第三个循环p是得到dp[j]的全部组合情况
第四个循环q是得到dp[i-j-1]的全部组合情况
#include <iostream>
#include<vector>
using namespace std;
class Solution {
public:
vector<string> generateParenthesis(int n) {
if (n == 0)return {""};
if (n == 1)return { "()" };
vector<vector<string>>dp(n+1);//二维数组dp[n]代表括号对数时的有效组合
dp[0] = { "" };//初始化条件
dp[1] = { "()" };//初始化条件
for (int i = 2; i <= n; i++)//开始计算i组括号时的括号组合
{
for (int j = 0; j < i; j++)// 开始遍历 p q ,其中p+q=i-1 , j 作为索引
{
for (int m = 0; m < dp[j].size(); m++)// p = j 时的括号组合情况
{
string p = dp[j][m];
for (int k = 0; k < dp[i - j - 1].size(); k++)// q = (i-1) - j 时的括号组合情况
{
string q = dp[i - j - 1][k];
string str = "(" + p + ")" + q;
dp[i].push_back(str);
}
}
}
}
return dp[n];
}
};
int main()
{
Solution s;
auto result = s.generateParenthesis(4);
for (int i = 0; i < result.size(); i++)
{
cout << result[i] << " ";
}
cout << endl;
std::cout << "Hello World!\n";
}