Codeforces 1015 F- Bracket Substring (DP+kmp预处理)

题目链接
思路
定义题目给定的字符串叫s,长度为2n的字符串叫str。"(“表示+1,“)”表示-1,那么可以通过数字大小来表示括号匹配的数量,0为恰好匹配。
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]:匹配到字符串str第i位,括号匹配正负值为j,对应匹配到字符串s第k位的所有方案数。
f [ i ] [ 0 ] f[i][0] f[i][0]:在字符串s匹配到第i位,第i+1位填”)“时,在字符串s中所对应的位置。
f [ i ] [ 1 ] : f[i][1]: f[i][1]:在字符串s匹配到第i位,第i+1位填”(“时,在字符串s中所对应的位置。
f[i][0/1]也相当于求了一下next数组。可以看成当这一位填了”(“或”)"之后不再是s的子串,那么s的最长前缀和str的最长后缀就要更新一下,就是kmp的道理。

考虑转移方程,在字符串str中下一位需要填“(”或者")"
填"(“时: d p [ i + 1 ] [ j + 1 ] [ f [ k ] [ 1 ] ] + = d p [ i ] [ j ] [ k ] dp[i+1][j+1][f[k][1]]+=dp[i][j][k] dp[i+1][j+1][f[k][1]]+=dp[i][j][k]
填”)"时: d p [ i + 1 ] [ j − 1 ] [ f [ k ] [ 0 ] ] + = d p [ i ] [ j ] [ k ] ( j > 0 ) dp[i+1][j-1][f[k][0]]+=dp[i][j][k](j>0) dp[i+1][j1][f[k][0]]+=dp[i][j][k](j>0)

在预处理的过程中会发现设置了 f [ l e n ] [ 0 ] = f [ l e n ] [ 1 ] = l e n f[len][0]=f[len][1]=len f[len][0]=f[len][1]=len,是因为当整个s串已经匹配完全的时候,那么后面不管怎么加那么在s中所匹配的最长长度都是len。所以在最后的转移中,k一定要枚举到长度为len的情况。
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 200 + 10;
const int mod = 1e9 + 7;

char s[N];
int nxt[N], len;
int dp[N][N][N], f[N][2];

void kmp() {
    len = strlen(s + 1);
    for(int i = 2, j = 0; i <= len; i++) {
        while(j && s[j + 1] != s[i]) j = nxt[j];
        if(s[j + 1] == s[i]) j++;
        nxt[i] = j;
    }
    for(int i = 0; i < len; i++) {
        int p = i, q = i;
        while(p && s[p + 1] != ')') p = nxt[p];
        while(q && s[q + 1] != '(') q = nxt[q];
        if(s[p + 1] == ')') p++;
        if(s[q + 1] == '(') q++;
        f[i][0] = p;
        f[i][1] = q;
    }
    f[len][0] = f[len][1] = len;
}

void solve() {
    int n, m;
    scanf("%d%s", &n, s + 1);
    m = n * 2;
    kmp();
    dp[0][0][0] = 1;
    for(int i = 0; i < m; i++) {
        for(int j = 0; j <= n; j++) {
            for(int k = 0; k <= len; k++) {
                dp[i + 1][j + 1][f[k][1]] = (1LL * dp[i + 1][j + 1][f[k][1]] + dp[i][j][k]) % mod;
                if(j) dp[i + 1][j - 1][f[k][0]] = (1LL * dp[i + 1][j - 1][f[k][0]] + dp[i][j][k]) % mod;
            }
        }
    }
    printf("%d\n", dp[m][0][len]);
}

signed main() {
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值