Codeforces - 501 - div3 - Bracket Substring (KMP + DP)

Codeforces - 501 - div3 - Bracket Substring (KMP + DP)

题目链接:

http://codeforces.com/contest/1015/problem/F

题意:

  • 给你一个 n n 和一个字符串 s

问:构造一个长度为 2n 2 ∗ n 并包含 s s 的字符串的构造方法

输入:

5
()))()


3
(()


2
(((

输出:

5

4

0

数据范围:

  • 1<=n<=100

    • 1<=|s|<=200 1 <= | s | <= 200
    • 解题思路:

      • 状态: dp[i][j][k] d p [ i ] [ j ] [ k ] 代表构造长度为 i i , 多出 j [(] [ ( ] , 包含 s s 长度为 k 的前缀的字符串方案数。
      • 转移:为了转移,我们需要再求出一个东西 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 长度为 nk 的前缀
      • 初始化: dp[0][0][0]=1 d p [ 0 ] [ 0 ] [ 0 ] = 1
      • 最后答案: dp[2n][0][len] d p [ 2 ∗ n ] [ 0 ] [ l e n ]
      • 复杂度: O(n3) O ( n 3 )

      以下是心路历程:
      刚开始写时,直接就写了枚举 s 放在不同的位置,然后其他位置先填问号,这样就行成一个长度为 2n 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;
      }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值