Codeforces Round #343 (Div. 2)C. Famil Door and Brackets DP

转自:Codeforces Round #343 (Div. 2) 解题报告
题意:给你一个由括号组成的字符串,长度为m,现在希望获得一个长度为n(全由括号组成)的字符串,0<=n-m<=2000

这个长度为n的字符串要求有两个性质:

就是任意前缀,左括号数量大于右括号数量
字符串中左括号的数量等于右括号

现在让你可以在长度为m的原串前加一个括号串p,在原串后加一个括号串q 最后p+m+q=n

问有多少种组合p,q能得到目标串

题目思路:

定义dp[i][j],为前缀长为i,且左括号数量-右括号数量=j的串有多少个

算出s段左括号与右括号的差值记为cnt,记录p段至少需要的左括号数目为need。

然后枚举p的长度和平衡值 对于长度i, 当-d<=j时,p可以加到前面

然后当p确定后,q的长度也确定,因为最终 左=右 ,所以q 的(右-左)的代价也知道了

假设当前是i,平衡度是j,所以只要将dp[i][j]*dp[n-m-i][j+cnt]加到答案就行了

注意:dp[i][j]代表前缀i,平衡度为j的方案数, dp[n-m-i][j+cnt]为后缀n-m-i,平衡度为-(j+cnt)的方案数,是对称的,很重要
即:dp[n - m - i][j + cnt] = dp[n - m - i][-(j + cnt)] // dp第二维不可为负数

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2010;
const int mod = 1e9 + 7;
const int INF = 10000000000;
int N, M;
char s[100010];
LL dp[maxn][maxn];
void add(LL &a, LL b) {
    a += b;
    if (a >= mod) {
        a -= mod;
    }
}
int main() {
    scanf("%d%d",&N,&M);
    scanf("%s",s+1);
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= N - M; i++) {
        for (int j = 0; j <= i; j++) {
            add(dp[i][j],dp[i-1][j+1]);
            if (j > 0) {
                add(dp[i][j],dp[i-1][j-1]);
            }
        }
    }
    int minv = INF;
    int num = 0;
    for (int i = 1; i <= M; i++) {
        if (s[i] == '(')num++;
        else num--;
        minv = min(minv,num);
    }
    LL ans = 0;
    for (int i = 0; i <= N - M; i++) {
        for (int j = 0; j <= i; j++) {
            if (j + minv >= 0 && j + num <= N - M - i) {
                add(ans,dp[i][j]*dp[N-M-i][j+num]%mod);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值