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

来源:互联网 发布:nba球员鞋垫有数据 编辑:程序博客网 时间:2024/06/06 16:53

题意:给定一个由'(' , ')' , '[' , ']'四个字符组成的字符串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 105char 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);}


0 0