DP。
具体做法:dp[i][j]表示长度为 i 的括号串,前缀和(左括号表示1,右括号表示-1)为 j 的有几种。
状态转移很容易得到:dp[i][j]=dp[i - 1][j + 1]+dp[i - 1][j - 1],表示 i 这位分别放上右括号和左括号。
然后就是要处理题目的问题了:
我们可以枚举P串的长度和P串的前缀和,来看这种情况下是否符合题目要求,如果符合答案增加。
那么如何判断P串长度为left,前缀和为p的情况下,有几种符合题目要求呢?
先对已经存在的那个S串做一次括号匹配,做完之后剩下的肯定是 L个右括号+R个左括号
接下来的过程都是从左往右看P串,从右往左看Q串
我们假设P串的前缀和是p,S串的前缀和是k,很容易得到k=R-L;我们根据p和k可以推出Q串的前缀和是p+k(注意:Q串是从右往左看的)
我们需要保证p+k>=0&&p+k<=n-m,此外还需要保证 P串的前缀和 - L >=0,即p - L >= 0,Q串前缀和 - R>=0,即p + k - R >= 0
满足上述几个条件,答案增加 dp[left][p] * dp[right][p + k]
题外话:卡特兰数第n项,就是dp[2*n][0]。
#include<cstdio> #include<cstring> #include<cmath> #include<string> #include<vector> #include<queue> #include<algorithm> #include<iostream> using namespace std; const int maxn = 100000 + 10; char s[maxn]; char st[maxn]; int top; long long dp[2000 + 10][2000 + 10]; long long MOD = 1e9 + 7; int n, m; long long ans; long long k; void read() { scanf("%d%d", &n, &m); scanf("%s", s); } void init() { memset(dp, 0, sizeof dp); memset(st, 0, sizeof st); ans = 0; k = 0; top = -1; } void work() { for (int i = 0; s[i]; i++) { if (top == -1) st[++top] = s[i]; else { if (st[top] == '('&&s[i] == ')') st[top] = 0, top--; else st[++top] = s[i]; } } int L = 0, R = 0; for (int i = 0; st[i]; i++) { if (st[i] == ')') L++; else break; } R = strlen(st) - L; k = R - L; dp[0][0] = 1; for (int i = 1; i <= n - m; i++) { for (int j = 0; j <= n - m; j++) { if (j + 1 <= n - m) dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % MOD; if (j - 1 >= 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % MOD; } } for (int left = 0; left <= n - m; left++) { int right = n - m - left; for (int p = 0; p <= left; p++) { if (p + k >= 0 && p - L >= 0 && p + k - R >= 0 && p + k <= n - m) ans = (ans + (dp[left][p] * dp[right][p + k]) % MOD) % MOD; } } printf("%lld ", ans); } int main() { read(); init(); work(); return 0; }