题目求的是合法的括号序列数量,那么必然左括号和右括号数量相等,都是字符串长度的二分之一,设为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; }