括号匹配 | 2022.8.22

一、题目描述

e7d2048531b1496799dee1cae16b6a20.png

测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. 7↵
  2. )())↵
  3. )↵
  4. ((↵
  5. ((↵
  6. (↵
  7. )↵
  8. )↵
以文本方式显示
  1. 2↵
1秒64M0
测试用例 2以文本方式显示
  1. 2↵
  2. (())↵
  3. ()↵
以文本方式显示
  1. 1↵
1秒64M0

 二、题目分析

(Ⅰ)什么样的括号序列是有用的(可解的)?

        比如说“((”可以和“))”搭配成为匹配的括号序列,称序列“((”和序列“))”均是有用的,或者说是“可解的”。但是像“)(”这样的序列,没有任何一个序列可以和它匹配,称“)(”就是“不可解的”。

(Ⅱ)怎么处理括号序列?

        合法的括号序列指的是每一个左括号都有对应一个右括号,而且这个左括号应该在其对应的右括号的左边。

        首先,我们来看,假定给你一段括号序列,你怎么判断它是“合法的”?

        合法的括号序列例如:((()())()) == ( (  () ()  )  () )

        不难知道,如果一段括号序列是“合法的”,它的一个必要条件是左括号的数量等于右括号的数量,而且进一步看,如果我们从左到右数起,左括号的数量一定恒是大于等于右括号的数量的。

(不妨想象一下,如果出现左括号的数量小于右括号,那么一定是非法的)

例如())(,虽然整体上看左括号的数量等于右括号的数量,但是从左往右看下去:

))(:一个左括号,零个右括号,1>0,没有问题。

())(:一个左括号,一个右括号,1=1,没有问题。

()):一个左括号,两个右括号,1<2,出现了左括号数量小于右括号数量,此时就可以断定这个序列“非法”。

        如何表达“大于等于”这个关键信息呢?

        用我们小学二年级就学过的正负数哇。

        我们约定左括号''=+1,右括号''=-1。

        用这个方法,判断括号序列是否可解:

        判断一个括号序列是可解的:如果一个括号序列从左向右计算的过程中恒大于等于0,或者从右向左计算的过程中恒小于等于0,那么这个序列是可解的。

        判断一个括号序列是不可解的:如果一个括号序列在从左向右计算的过程中出现了负值,在从右向左计算的过程中又出现了正值,就说明这个括号序列是不可解的。        

        假设括号序列是“有解的”,那么用计算的方法还能帮助化简括号序列

        如果最终序列=0,那么该括号序列“合法”;

        如果最终序列>0,那么该括号序列有多余的左括号;

        如果最终序列<0,那么该括号序列有多余的右括号。

        

        举例来说,现在给你的括号序列是这样的:()(

        从左向右依次计算,()( <==> +1 -1 +1 = +1 <==>

        事实上,()(就是等价的。这种化简方法可以很方便地处理不同的括号序列。

(Ⅲ)满足什么条件的两个序列可以互相匹配?

        两个可解的括号序列,如果它们的和为0,那么就是相互匹配的。


三、思路

代码分为五个部分,

①读入n行括号序列;

②化简n行括号序列;

③通过计算的方法判断括号序列是否可解;

④如果可解,那么计算这个括号序列的值;

⑤通过枚举的方法判断是否有可以相互匹配的序列。 


以用例1为例,对七组序列处理成下述表格:

 然后就可以一眼看出-2和2匹配,-1和1匹配,总共两组。


四、代码实现

首先引入函数

int valuate(char c) {
	if ( c == '(' ) return 1;
	else if ( c == ')' ) return -1;
}
//函数valuate的作用是将‘(’换算为1,将‘)’换算为-1

还需要

#include <stdio.h>     
#include <string.h>     
#include <math.h>
#define N 100010
long int solvable[N],value[N];
char brackets[N];
//solvable[i]只能为0或1,如果是0,表明第i组括号序列不可解;如果是1,表明第i组括号序列可解。
//value[i]是计算出来的括号序列的值。
//brackets临时存储读入的括号序列。

主函数main分为三部分:输入化简并计算判断匹配

输入部分

int n,score=0;
	scanf("%d",&n);
for (int i=0;i<n;i++) {
		scanf("%s",&brackets);

在输入brackets的同时进行化简并计算的操作

for ( int i=0 ; i<n ; i++ ) {
		scanf("%s",&brackets);
		int len = strlen( brackets );
		
//初始时的value值为0,并且默认为合法。
		value[i] = 0;
		solvable[i] = 1; //solvable值为1表示合法

		int partial_min = 0;
//计算括号序列的逐项值,并且记录该过程中出现的最小负值(如果有的话)
		for ( int j=0 ; j<len ; j++ ) {
			value[i] = value[i] + valuate( brackets[j] );
			if ( value[i] < partial_min ) {
				partial_min = value[i];
			}
		}
		
//如果计算过程中出现了负值,需要判断这是属于无解的情况还是属于全是右括号的情况。
		if ( partial_min < 0 ) {
			int partial_max = 0;
			for ( int j = len-1 ; j>=0 ; j-- ) {
				partial_max = partial_max + valuate( brackets[j] );
//如果从右向左逐项计算的过程中出现了正值,那么说明无解,将solvable置为0,0表示无解。
				if ( partial_max > 0 ) {
					solvable[i] = 0;
					break;
				}
			}
		}

然后接着判断是否匹配

首先定义M

int M = ( all_max + all_min ) > 0 ? all_max : (0-all_min) ;

all_max是所有value中最大的那一项正值,all_min是所有value中最小的那一项负值。

int count_positive , count_negative;
//count_positive记录正值的数量,count_negative记录负值的数量

	for ( int i=0 ; i<=M ; i++) {
		count_positive = 0 ;
		count_negative = 0 ;
		for ( int j=0 ; j<n ; j++ ) {
			if ( solvable[j] == 1 && ( abs(value[j]) == i ) ) {
				if ( value[j] >= 0 ) count_positive++;
				else count_negative++;
			}
		}
		if ( i == 0 ) score+= (count_positive/2);
		else score+= ( (count_positive-count_negative)>0 ? count_negative:count_positive );
	}

 注意,我这里匹配方法效率低下,完全可以优化。不过就这道题目而言,足够AC了。


五、完整代码

#include <stdio.h>     
#include <string.h>     
#include <math.h>
#define N 100010
long int solvable[N],value[N];
char brackets[N];

int main() {    
    
    int valuate(char);
      
	int n,score=0;
	scanf("%d",&n);

	int all_max=0,all_min=0;
	for (int i=0;i<n;i++) {
		scanf("%s",&brackets);
		int len = strlen(brackets);
		
		value[i]=0;
		solvable[i]=1;
		int partial_min = 0;
		for (int j=0;j<len;j++) {
			value[i] = value[i] + valuate(brackets[j]);
			if ( value[i] < partial_min ) {
				partial_min = value[i];
			}
		}
		
		if (partial_min<0) {
			int partial_max = 0;
			for (int j=len-1;j>=0;j--) {
				partial_max = partial_max + valuate(brackets[j]);
				if (partial_max>0) {
					solvable[i]=0;
					break;
				}
			}
		}
		
		if (solvable[i]==1) {
			if (value[i]<all_min) all_min=value[i];
			if (value[i]>all_max) all_max=value[i];
		}
	}
	
	int M = ( all_max + all_min ) > 0 ? all_max : (0-all_min) ;
	
	int count_positive,count_negative;
	for (int i=0;i<=M;i++) {
		count_positive=0;
		count_negative=0;
		for (int j=0;j<n;j++) {
			if (solvable[j]==1 && ( abs(value[j])==i) ) {
				if (value[j]>=0) count_positive++;
				else count_negative++;
			}
		}
		if (i==0) score+=(count_positive/2);
		else score+= ( (count_positive-count_negative)>0?count_negative:count_positive );
	}
	
	printf("%d\n",score);
	
	
	
	

	return 0; 
}  

int valuate(char c) {
	if ( c == '(' ) return 1;
	else if ( c == ')' ) return -1;
}


不知道写清楚了没有,有些东西确实——

emm——

比较抽象——

比较个人化的一些东西——

再加上一个语文不好的人写出来的不通顺的一些语句——

能看懂挺好,看不懂的话就算了,独乐乐也行欸嘿~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值