poj 1141 dp(补成最短的规范字符串)

题意:给定一个由'(' , ')' , '[' , ']'四个字符组成的字符串s,求一个最短的合乎括号规范的字符串t,使得s是t的子序列。合乎规范的串的定义是:1. 空串。 2. 若S合乎规范, 那么(S) 和[S] 都合乎规范。3. 如果A 和 B 都合乎规范,那么AB 合乎规范。

Sample Input
([(]
Sample Output
()[()]

思路:

采用动态规划的思想。用二维数组dp[i][j]表示s[i...j]这个串变为规范的需要增加的最少字符数量。对于一个串s[l,r],分类找到子问题:

1、如果s[l]==s[r],那么dp[i][j] = dp[i+1][j-1];

2、遍历[l,r]的分割点k,求出dp[l,k]+dp[k+1][r],找到这些(包括1里面)中最小的作为dp[i][j]。

终止条件:当l==r时,只能单独匹配这一个字符。

用一个数组f记录dp[i][j]取得最小值时的动作。f数组的含义:
-1:单独匹配
-2:两边匹配
非负数:中间匹配点

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 105
char s[N],res[N<<1];
int dp[N][N],f[N][N],len;/*f数组的含义
                          -1:最左边单匹配
                          -2:两边匹配
                          非负数:中间匹配点*/
int match(char a,int b){                //判断a和b是否为一对匹配的括号
    return (a=='('&&b==')')||(a=='['&&b==']');
}
int solve(int l,int r){                 //记忆化dp
    int tmp;
    if(l>r)
        return 0;
    if( dp[l][r] != -1)
        return dp[l][r];
    if(l == r){                         //单独字符,只能增加一个相称字符予以匹配
        f[l][r] = -1;
        return dp[l][r] = 1;
    }
    dp[l][r] = INF;
    if(match(s[l],s[r])){               //如果两边的能够匹配
        tmp = solve(l+1, r-1);
        if(tmp<dp[l][r]){
            f[l][r] = -2;
            dp[l][r] = tmp;
        }
    }
    for(int i = l;i<r;i++){             //遍历中间分隔点
        tmp = solve(l, i)+solve(i+1, r);
        if(tmp < dp[l][r]){
            dp[l][r] = tmp;
            f[l][r] = i;
        }
    }
    return dp[l][r];
}
char ano(char x){                       //返回与输入括号相称的另一半
    if(x=='(')
        return ')';
    if(x=='[')
        return ']';
    if(x==')')
        return '(';
    return '[';
}
void print(int x,int y,int l,int r){//在结果串res[x...y]中输出与原来串s[l...r]对应的部分
    if(l>r || x>y)
        return;
    if(f[l][r] == -1){              //单独匹配
        if(s[l] == '(' || s[l] == '['){
            res[x] = s[l];
            res[x+1] = ano(s[l]);
        }else{
            res[x] = ano(s[l]);
            res[x+1] = s[l];
        }
        print(x+2, y, l+1, r);
    }else if(f[l][r] == -2){        //两边匹配
        res[x] = s[l];
        res[y] = s[r];
        print(x+1, y-1, l+1,r-1);
    }else{                          //中间隔开
        print(x, x+dp[l][f[l][r]]+f[l][r]-l, l, f[l][r]);
        print(x+dp[l][f[l][r]]+f[l][r]-l+1, y, f[l][r]+1, r);
    }
}
int main(){
    int j;
    clc(dp, -1);
    clc(f, 0);
    scanf("%s",s);
    len = (int)strlen(s);
    j = solve(0,len-1);
    print(0,len-1+j,0,len-1);
    res[len+j] = '\0';
    printf("%s\n",res);
}


你可能感兴趣的:(poj 1141 dp(补成最短的规范字符串))