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

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

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

在预处理的过程中会发现设置了\(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;
}
posted @ 2021-02-18 17:38  这知识他不进我的脑子  阅读(58)  评论(0编辑  收藏  举报