题目求的是合法的括号序列数量,那么必然左括号和右括号数量相等,都是字符串长度的二分之一,设为n。这里首先排除字符串长度是奇数的情况,直接输出0。
我们使用动态规划,令dp[i]表示左括号比右括号多i个的数量,那么i的取值范围可以是0-n。
初始化状态,左右括号都为0个,只有这一种情况,dp[0] = 1。
接下来遍历字符串。分别对字符 ( ) ? 进行处理,得到状态转移方程。
当字符为 ( 时,dp[i + 1] = dp[i]。i的取值范围是 i < n。
当字符为 ) 时,dp[i - 1] = dp[i]。i的取值范围是 i > 0。
当字符为 ? 时,dp[i] = dp[i - 1] + dp[i + 1]。注意这里要对i的边界条件进行额外的判断和处理。
很显然我们不能直接对dp进行操作,因为会把dp原本的数据给覆盖掉。因此可以用一个临时的dp1来存储新的dp数据,当状态转移全部完成后,再让dp = dp1。
c++这里建议dp和dp1用map这个容器,其他语言的话用key-value字典类型的容器,哈希表也可以,这样能够有效节省遍历容器时的时间复杂度,没必要每次都遍历0-n的所有项。
最后输出dp[0]即可,下面是代码。
#include <iostream>
#include <map>
using namespace std;
const int mod = 1e9 + 7;
int main() {
string str;
cin >> str;
if (str.size() & 1) {
cout << 0;
return 0;
}
int n = str.size() / 2;
map<int, long long> dp;//dp[i]表示左括号比右括号多i个的数量
dp[0] = 1; //初始时左右括号都为0个,相差也是0个。只有着一种情况
for (char& c : str) {
map<int, long long> dp1;
switch (c) {
case '(':
for (auto [k, v] : dp) {
if (k < n) dp1[k + 1] = v;
}
break;
case ')':
for (auto [k, v] : dp) {
if (k) dp1[k - 1] = v;
}
break;
default:
for (auto [k, v] : dp) {
if (k) dp1[k - 1] = (dp1[k - 1] + v) % mod;
if (k < n) dp1[k + 1] = v;
}
break;
}
dp = dp1;
}
cout << dp[0] << endl;
}



京公网安备 11010502036488号