Codeforces - 501 - div3 - Bracket Substring (KMP + DP)
题目链接:
http://codeforces.com/contest/1015/problem/F
题意:
- 给你一个 n n 和一个字符串
问:构造一个长度为 2∗n 2 ∗ n 并包含 s s 的字符串的构造方法
输入:
5
()))()
3
(()
2
(((
输出:
5
4
0
数据范围:
-
- 1<=|s|<=200 1 <= | s | <= 200
解题思路:
- 状态: dp[i][j][k] d p [ i ] [ j ] [ k ] 代表构造长度为 i i , 多出 个 [(] [ ( ] , 包含 s s 长度为 的前缀的字符串方案数。
- 转移:为了转移,我们需要再求出一个东西
nxt[i][j]
n
x
t
[
i
]
[
j
]
。
- nxt[i][j] n x t [ i ] [ j ] : 当第 i 个字符填 0 [ ) ] 或 1 [ ( ] 时与 s 前缀匹配的最长长度。这个可以通过 KMP K M P 的 fail 数组得到。
- dp[i+1][nj][nk]=(dp[i+1][nj][nk]+dp[i][j][k]) d p [ i + 1 ] [ n j ] [ n k ] = ( d p [ i + 1 ] [ n j ] [ n k ] + d p [ i ] [ j ] [ k ] )
- nj=j−(p==0?1:−1); n j = j − ( p == 0 ? 1 : − 1 ) ; 若第 i + 1 个字符放 [(] [ ( ] 那么多出来的 [)] [ ) ] 减一,相反会加一
- nk=nxt[k+1][p]; n k = n x t [ k + 1 ] [ p ] ; 就是相当于包含 s s 长度为 的前缀
- 初始化: dp[0][0][0]=1 d p [ 0 ] [ 0 ] [ 0 ] = 1
- 最后答案: dp[2∗n][0][len] d p [ 2 ∗ n ] [ 0 ] [ l e n ]
- 复杂度: O(n3) O ( n 3 )
以下是心路历程:
刚开始写时,直接就写了枚举 s 放在不同的位置,然后其他位置先填问号,这样就行成一个长度为 2∗n 2 ∗ n 的新串,然后 DP D P 求出这个新串合法的方案数,最后答案再全部相加。样例一过,自信一交,直接 WA 掉….后来才发现这样的写法是会有重复的。比如:()?? 和 ??()AC代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <cmath> #include <cstdlib> #include <ctime> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) const LL mod = 1e9 + 7; const int maxn = 205; int fail[maxn + 5]; int nxt[maxn + 5][2];// 当第 i 个字符填 0 [ ) ] 或 1 [ ( ] 时与 s 前缀匹配的最长长度。 LL dp[maxn + 5][maxn + 5][maxn + 5]; // 构造长度为 i , 多出 j 个 [ ( ] , 包含 s 长度为 k 的前缀的字符串方案数 char s[maxn + 5]; int n,len; void Get_fail(){ fail[0] = -1; int j = 0,k = -1; while(j <= len){ if(k == -1 || s[j] == s[k]) fail[++j] = ++k; else k = fail[k]; } //rep(i,0,len) printf("%3d%c",fail[i], " \n"[i == len]); } void Get_nxt(){ rep(i,0,len){ rep(j,0,1){ if(i == len) nxt[i + 1][j] = len; else{ char ch = (j == 0 ? ')' : '('); int r = i; while(r >= 0 && s[r] != ch) r = fail[r]; if(s[r] == ch || r == -1) r++; nxt[i + 1][j] = r; } } } //rep(i,0,len) printf("%3d%c",nxt[i][0], " \n"[i == len]); //rep(i,0,len) printf("%3d%c",nxt[i][1], " \n"[i == len]); } LL DP(){ dp[0][0][0] = 1; rep(i,0,2 * n){ rep(j,0,min(i,n)){ rep(k,0,len){ rep(p,0,1){ if(dp[i][j][k] == 0) continue; int nj = j - (p == 0 ? 1 : -1); if(nj >= 0 && nj <= n){ int nk = nxt[k + 1][p]; dp[i + 1][nj][nk] = (dp[i + 1][nj][nk] + dp[i][j][k]) % mod; } } } } } return dp[2 * n][0][len]; } void Init(){ len = strlen(s); //rep(i,0,len) printf("%3d%c",i, " \n"[i == len]); //rep(i,0,len) printf("%3c%c",s[i], " \n"[i == len]); Get_fail(); Get_nxt(); } int main(){ scanf("%d",&n); scanf("%s",s); Init(); printf("%lld\n",DP()); return 0; }
WA代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long LL; const LL mod = 1e9 + 7; const int maxn = 205; int dp[maxn + 5][maxn + 5]; LL sum; void Solve(string s){ int len = s.size(); memset(dp,0,sizeof(dp)); dp[0][1] = 1LL; for(int i = 1;i < len;i++){ for(int j = 0;j < len;j++){ if(s[i] == '('){ if(j == 0) dp[i][j] = 0LL; else dp[i][j] = dp[i - 1][j - 1]; } else if(s[i] == ')') dp[i][j] = dp[i - 1][j + 1]; else if(s[i]=='?'){ if(j == 0) dp[i][j] = dp[i - 1][j + 1]; else dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j + 1]) % mod; } } } sum = (sum + dp[len - 1][0]) % mod; //printf("%d\n",dp[len-1][0]); } string k[maxn + 5]; void Init(){ sum = 0LL; k[0] = ""; for(int i = 1;i <= 201;i++) k[i] = k[i - 1] + "?"; } int main(){ Init(); int n,len; string h,s; cin >> n; cin >> h; n *= 2; len = h.size(); for(int i = 1;i <= n - len + 1;i++){ s = k[i - 1] + h + k[n - len - (i - 1)]; Solve(s); //cout << s << endl; } printf("%lld\n",sum); return 0; }