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

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

题目链接:

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

题意:

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

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

输入:

5
()))()


3
(()


2
(((

输出:

5

4

0

数据范围:

  • 1<=n<=100 1 <= n <= 100
  • 1<=|s|<=200 1 <= | s | <= 200

解题思路:

  • 状态: dp[i][j][k] d p [ i ] [ j ] [ k ] 代表构造长度为 i i , 多出 j j [(] [ ( ] , 包含 s s 长度为 k 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 n k 的前缀
  • 初始化: 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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
#include
#include
#include
#include
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;
}

你可能感兴趣的:(ACM - 动态规划)