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;
}