正在网易云课堂学习王宏志老师的算法设计与分析入门篇课程视频,将学习中的作业问题发上来与大家一同讨论。这篇是对第四周的作业第二题个人的一些思路,希望与大家一同学习。
2括号子序列 - A(10分)
题目内容:
一段括号序列被称为平衡的,如果对于任意前缀,左括号的数目都不小于右括号。
给定一段括号序列,问有多少括号子序列是平衡的。(内容相同但位置不同的算两种。)
输入格式:
多组测试数据,每组测试数据包含一行一个括号序列,括号序列的长度 <= 100。
输出格式:
输出一行表示答案模 10^9 + 7 后的结果。
输入样例:
(()())
()()
输出样例:
18
4
Hint:
样例一:
()__
(_)_
(__)
()__
(_)_
(__)
_()_
_(_)
()()
()()
()()
()()
((__))
(())
(()__)
(__())
(())
(()())
时间限制:2000ms内存限制:128000kb
做了这道题感觉自己对动态规划根本就不了解,先是想了一个时间复杂度为2^n的方法,通过记录每左括号后面的有括号个数,计算求解。
思路与代码结果都有问题,时间也超了。
问大四学长的思路:
定义dp[i][j][k]
表示当前我要处理字符串中第i个字符,
并且前面已经取了j个左括号和k个右括号。
由于题意要满足任意前缀,左括号的数目都不小于右括号
所以这里限定j>=k。
最终答案就是把所有dp[n][i][i]累加起来,这里i代表左括号个数
初始状态dp[0][0][0]=1,表示空串。
那么当前处理第i个字符
一种是忽略这个字符,dp[i+1][j][k] += dp[i][j][k]
一种是选择这个字符,那么就要判断这个字符是左还是右。
如果是左,直接累加,dp[i+1][j+1][k] += dp[i][j][k]
否则要先看如果加了这个右括号,会不会违反j>=k的规则,不违反则dp[i+1][j][k+1] += dp[i][j][k],违反就不管了
自己想了一下,大概有点明白。但第二天仔细研究,用递归做的话,时间复杂度也是2^n。
递归方程是 T(n) = 2*T(n-1) + o1;
用公式没求明白,但展开看的话还是很明显的
T(n) = 2 * T(n-1) + o1
= 2^2 * T(n-2) + ( 2 + 1) * o1
…….
= 2^(n-1)T(1) + (2^(n-2) + ……+ 2 + 1) o1 = o2^n
后来发现用循环来做时间复杂度仅为n^3。
代码如下:
int mo = 1000000007;
long a[101][101][101];
void jisuan(char*p, int n) {
for (int i = 0; i < n; i++){
for (int j = 0; j <= i; j++){
for (int k = 0; k <= j; k++){
if (a[i][j][k] == 0)
break;
a[i + 1][j][k] = (a[i][j][k] + a[i + 1][j][k]) % mo;
if (p[i] == '('){
a[i + 1][j + 1][k] = (a[i][j][k] + a[i + 1][j + 1][k]) % mo;
}
else
if (j > k){
a[i + 1][j][k + 1] = (a[i][j][k] + a[i + 1][j][k + 1]) % mo;
}
}
}
}
}
//要注意这次是多组输入,对于每次输入要重新初始化。
void chushihua(int n){
for (int i = 0; i <= n; i++){
for (int j = 0; j <= i; j++){
for (int k = 0; k <= j; k++){
a[i][j][k] = 0;
}
}
}
a[0][0][0] = 1;
}
最终结果就是所有a[n][i][i]的和,n为字符个数,i从1到n/2。因为n个字符,平衡的子序列字符数小于等于n,其中左括号右括号相等,所以i<=n/2即可。