动态规划 括号匹配(二)问题

 

   括号匹配(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:6

描述

给你一个字符串,里面只包含”(“,”)”,”[“,”]”四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。

如:

[]是匹配的

([])[]是匹配的

((]是不匹配的

([)]是不匹配的

输入

第一行输入一个正整数N,表示测试数据组数(N<=10)

每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100

输出

对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行

样例输入

4

[]

([])[]

((]

([)]

样例输出

0

0

3

2

来源

《算法艺术与信息学竞赛》

上传者

张云聪

     AC 0.32

 

一开始用栈来做,判断是否匹配,不匹配则计数+1,计算出不匹配个数。这种算法处理不了很多数据。

然后又改成两端向内依次寻找匹配,匹配则删去然后再寻找这个匹配里面的括号。一部分匹配完后,继续在没有匹配的数据中寻找,直到所有数据都被处理。这个算法比上面的好一点,但是不能处理()()或([)(])这样的特殊数据。

最后查到了用动态规划来做,然后又设计了第三种算法,成功AC。

 

动态规划思路:

把问题分为几个阶段,分别求解,将每一次运算的结果保存在一个二维数组中,以后的求解过程可以使用之前的运算结果。

 

本题思路:

可以把字符串分解,有以下这几种情况:

1.S=”(“等,此时dp=1.

2.S=”(S’)”,S需要的最小括号数=S’

3.S=”S’1+S’2″,S需要的最小括号数=S’1的+S’2的

 

 

1.首先,建立一个二位数组dp[][]来存储结果,dp[i][j]表示从S[i]到S[j]的最少添加括号个数。

2.那么首先可以把dp[i][j](i==j)的值存为1,因为一个字符必定需要添加一个括号。其他值先赋值为0。(阶段0)

3.然后,进行循环遍历,每阶段考虑k个长度的区间(k从1到总长),每次考虑i,j内的区间(i从0开始依次考虑),如果S[i]==S[j],说明他们是匹配的,dp[i][j]=dp[i+1][j-1](括号里面部分需要添加的最小括号数)

4.此外,该题还有分割情况(比如”[[[]]][[[]]]”需要分成两部分考虑),应依次考虑所有分割情况,如果进行了某种分割后,最多需要添加括号更少,就采用更小的值,所以有for(int k = i;k<j;k++)dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);

5.全部处理完后,dp[0][length-1]即为S[0]S[length]也就是整个字符串需要添加的最小括号数。

又该题可以看出,动态规划实际遍历了所有可能结果,其中用二维数组保存一些结果,加快了运算速度。

 

手动演算”[((](]”的结果:

《动态规划 括号匹配(二)问题》

源码

#define R 105
#include<stdio.h>
#include<limits.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
bool match(char a,char b){
    if(a=='('&&b==')')return 1;
    if(a=='['&&b==']')return 1;
    return 0;
}
int min(int a,int b){
    if(a<b)return a;
    else return b;
}
int main(){
    int n;
    char all[R];
    int dp[R][R];
    int i,j,k,x;
    int len;
    scanf("%d",&n);
    while(n--){
        scanf("%s",all);
        len = length(all);
        for(i=0;i<len;i++)
            for(j=0;j<R;j++)
                dp[i][j]=0;
        for(i=0;i<len;i++)
            dp[i][i]=1;
        for(k=1;k<=len;k++){//k为阶段数,表示处理长度为k的区间
            for(i=0;i<len-k;i++){
                j=i+k;
                dp[i][j]=INT_MAX;
                if(match(all[i],all[j]))
                    dp[i][j]=dp[i+1][j-1];
                for(x=i;x<j;x++)
                    dp[i][j]=min(dp[i][j],dp[i][x]+dp[x+1][j]);
                //printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
            }
        }
        printf("%d\n",dp[0][len-1]);
    }
    return 0;
}

另外符上失败的两种算法(注意这两种是错的):

#include<stdio.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
int main(){
    int n,l,i,x,number;
    char all[105],S[105];
    scanf("%d",&n);
    while(n--){
        number = 0;
        x = 0;
        scanf("%s",all);
        l = length(all);
        for(i=0;i<l;i++){
            if(all[i]=='('||all[i]=='['){
                S[x] = all[i];
                x++;
            }
            else if(all[i]==')'&&S[x-1]=='('){
                x--;
            }
            else if(all[i]==']'&&S[x-1]=='['){
                x--;
            }
            else{
                number++;
            }
        }
        number += x;
        printf("%d\n",number);
    }
    return 0;
}
//该算法不足,放弃
#include<stdio.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
char match(char a){
    if(a == '(')
        return ')';
    else if(a == '[')
        return ']';
    else
        return '\0';
}
int main(){
    int n,l;
    int a,number;
    int i,j;
    char all[105];
    scanf("%d",&n);
    while(n--){
        number = 0;
        scanf("%s",all);
        l = length(all);
        a = l - 1;
        for(i=0;i<l;i++){
            //printf("i==%d",i);
            if(all[i]==')'||all[i]==']'){
                number++;
            }
            else if(all[i]=='('||all[i]=='['){
                for(j=a;j>=i;j--){
                    if(all[j]==match(all[i])){
                        //printf("match %d == %d\n",i,j);
                        all[j]='\0';
                        a=j-1;
                        break;
                    }
                    else if(i==j){
                        number++;
                        break;
                    }
                }
            }
            else if(i>=a){
                while(all[++i]=='\0');
                i--;
                a = i;
                while(all[++a]!='\0'&&a<l);
                a--;
            }
        }
        printf("%d\n",number);
    }
    return 0;
}

 问题地址传送门

资料传送门

    原文作者:括号匹配问题
    原文地址: https://blog.csdn.net/wonder13579/article/details/81091657
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞