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