给定一个括号序列 (s),满足 (|s| = m)。你需要统计括号序列对 ((p, q)) 的数量。
其中 ((p, q)) 满足 (|p| + |s| + |q| = n),且 (p + s + q) 是一个合法的括号序列。
惨痛教训:不要形成思维定势!!!
一看到括号序列,满脑子就往栈去了,然后啥也没想出来就爆蛋了……
实际上,我们可以将整个括号序列抽象为如下形式:
[(
ightarrow 1 \
)
ightarrow -1
]
那么一个合法的括号序列要保证:
- 前缀和为 (0)
- 每一部分的前缀和不小于 (0)
然后我们可以设 (f_{i,j}) 表示长度为 (i) 的括号序列前缀为 (j) 的合法情况有多少个,然后一个非常简单的 (DP) 就能解决问题了。
代码:
# include <iostream>
# include <cstdio>
# define MAXN 100005
# define MAXM 2005
const long long MOD = 1e9+7;
char s[MAXN];
long long f[MAXM][MAXM];
// f[i][j] 长度为 i,前缀和为 j
// ( -> 1 ) -> -1
// 合法序列:j = 0,每个前缀和都不小于 0
int main(){
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", s+1);
f[0][0] = 1;
for(int i = 1; i <= n-m; i++){
for(int j = 0; j <= i; j++){
if(j > 0){
f[i][j] += f[i-1][j-1];
if(f[i][j] > MOD){
f[i][j] -= MOD;
}
}
if(j < i-1){
f[i][j] += f[i-1][j+1];
if(f[i][j] > MOD){
f[i][j] -= MOD;
}
}
}
}
int cur = 0, sum = 0;
for(int i = 1; i <= m; i++){
cur += s[i] == '(' ? 1 : -1;
sum = std::min(sum, cur);
}
long long ans = 0;
for(int i = -sum; i <= n-m; i++){
for(int j = -sum; j <= i; j++){
if(cur + j <= n-m-i){
ans = (ans + f[i][j] * f[n-m-i][cur+j]) % MOD;
}
}
}
printf("%lld", ans);
return 0;
}