括号匹配II(动态规划)

Description
给定一个只包含{},(),[]的字符串,假设你能够在这个括号
字符串中的任意位置添加任何括号,你要做得就是在合适的位置
添加括号,使得所有括号都正确嵌套,若所给字符串中的括号完
美匹配,无需修改,请输出“SZTU_WOD YYDS!”;否则求能使得这
串字符每对括号都正确嵌套的最少添加数。

Input
输入包含多组测试数据,每组测试样例占一行,每行不超过100个字符。

Output
若所给字符串中的括号完美匹配,无需修改,请输出“SZTU_WOD YYDS!”;
否则求能使得这串字符每对括号都正确嵌套的最少添加数。

Sample Input
()
())

Sample Output
SZTU_WOD YYDS!
1
#include<stdio.h>
#include<stack>
#include<map>
using namespace std;
char s[110];
int main(){//原先栈的做法
    int N,tot;
    scanf("%d",&N);
    while(N--){map<char,int>mp;
            stack<char>sign;
            tot=0;
            mp['(']=1;mp[')']=-1;
            mp['[']=2;mp[']']=-2;
            mp['{']=3;mp['}']=-3; //使用map来判断括号是否匹配
        scanf("%s",s);
        for(int i=0;s[i];i++){
            if(mp[s[i]]>0)sign.push(s[i]);//是左括号
            else{//右括号
                if(!sign.empty()){//栈非空
                    if(mp[sign.top()]+mp[s[i]]==0)sign.pop();//括号匹配
                    else tot++;//计数
                }
                else tot++;//栈空时
            }
        }
        while(!sign.empty()){//最后加上栈里的
            sign.pop();
            tot++;
        }
        printf("%d\n",tot);
    }
return 0;}

首先需要理解的是该题与原先我们使用栈做的括号匹配有什么区别

如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的

([(]是不匹配的

 ( [ ( ] 观察这个例子,我们按照该题的题意,我们可以很容易想到这个例子只需添加两个右括号即可满足题意,但是按照栈的做法,我们我们会发现得到的结果是4,因为按照栈的逻辑没有办法使得两个[ ]是得到匹配的,因此答案里多了2

那么我们只能寻找别的办法解决了

考虑使用动态规划解决

仔细观察题意我们不难发现,该题存在子问题结构

我们定义一个dp数组dp[ i ][ j ]表示下标从i到j的字符串需要添加的括号数量是多少(左闭右闭),而最小子问题就是 i = j时,此时只有一个括号必然需要添加一个括号进行匹配,其他情况可以初始化为0

那么如果下标为i和下标为j的括号是匹配的话,那么我们的当前问题就可以转换成dp[ i ][ j ]=dp[ i +1 ][ j - 1] 

反之,我们需要对区间[i,j]内的区间进行遍历分割,如果有更小的值,就采用更小的,也即
dp[i][j]=min(dp[i][j], dp[i][k] + dp[k+1][j]),因为存在这样的情况[ [ [ ] ] ] [ [ [ ] ] ]

至此我们就可以写代码了

#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;

char buf[105];
int dp[105][105];//定义dp数组dp[i][j]表示从i到j需要补充的括号数 (左闭右闭)
int maxn=0x3f3f3f3f;

bool check(char a,char b){
	return (a=='('&&b==')')||(a=='['&&b==']')||(a=='{'&&b=='}');
}

int main(){
	
	while((scanf("%s",buf))!=EOF){
		int len=strlen(buf);
		for(int i=0;i<len;i++){
			for(int j=0;j<=100;j++){
				dp[i][j]=0;
				if(i==j){
					dp[i][j]=1;//只有一个括号时必然需要补充一个括号 
				}
			}
		}
		
		for(int sizee=1;sizee<=len;sizee++){//从最小子问题开始求解 
			for(int i=0;i<len-sizee;i++){//起点 
				int j=i+sizee;//终点
				dp[i][j]=maxn;//因为下面需要求较小值,因此初始化为最大值
				if(check(buf[i],buf[j])){
					dp[i][j]=dp[i+1][j-1];//如果起点和终点匹配那么当前问题=去掉匹配的括号的子问题的解 
				} 
				//存在分割分割情况(比如"[[[]]][[[]]]"需要分成两部分考虑),应依次考虑所有分割情况
				for(int k=i;k<j;k++){
					dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
				} 
			}
		} 
		if(dp[0][len-1]==0){
			printf("SZTU_WOD YYDS!\n");
		}else{
			printf("%d\n",dp[0][len-1]);
		}
	}
		
	return 0;
} 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值