【BIT2021程设】10.括号匹配

写在前面:

本系列博客仅作为本人十一假期过于无聊的产物,对小学期的程序设计作业进行一个总结式的回顾,如果将来有BIT的学弟学妹们在百度搜思路时翻到了这一条博客,也希望它能对你产生一点帮助(当然,依经验来看,每年的题目也会有些许的不同,所以不能保证每一题都覆盖到,还请见谅)。

不过本人由于学艺不精,代码定有许多不足之处,欢迎各位一同来探讨。

同时请未来浏览这条博客的学弟学妹们注意,对于我给出完整代码的这些题,仅作帮助大家理解思路所用(当然,因为懒,所以大部分题我都只给一个伪代码)。Anyway,请勿直接复制黏贴代码,小学期的作业也是要查重的,一旦被查到代码重复会严厉扣分,最好的方法是浏览一遍代码并且掌握相关的要领后自己手打一遍,同时也要做好总结和回顾的工作,这样才能高效地提升自己的代码水平。

加油!


成绩10开启时间2021年08月27日 星期五 12:00
折扣0.8折扣时间2021年09月3日 星期五 23:00
允许迟交关闭时间2021年10月10日 星期日 23:00

Description

DarkDown在乐学上出了一道给定括号序列,判断其合法性的问题。

括号序列是由左括号“(”和右括号“)”组成的非空序列。对于一个括号序列很容易判定其合法性。比如“()”、“(())()”、“(()())”、“(()(()))”、“()()()”都是合法的,而“)”、“(”、“(()”、“(()))(”都是非法的。

Roark看了一眼题,立刻去饮水机处接了一杯水。

DarkDown意识到他是在暗示这道题太水了,于是立刻把题改了改,增加了一、、难度。

给定n个括号序列,两两配对,问最多能组成多少对合法括号序列。(每一个括号序列只能在一对中出现)

Roark接完水回来再看了看新题,开始挠头了,快帮帮他!

Input

第一行输入整数 n(1\leq n\leq 1e5)表示有n个括号序列。

接下来n行,每行输入一个只由“(”和”)“构成的字符串s_i 。(字符串长度满足1\leq \left |s_i \right |\leq 1e5)

所有字符串长度总和满足\sum \left |s_i \right |\leq 5e5

Output

输出一个整数,表示最大的合法括号序列对数。

Hint

第一组用例可以组成2对合法括号序列,分别是“((   )())”、“(   )”。

测试用例 1以文本方式显示
  1. 7↵
  2. )())↵
  3. )↵
  4. ((↵
  5. ((↵
  6. (↵
  7. )↵
  8. )↵
以文本方式显示
  1. 2↵
1秒64M0
测试用例 2以文本方式显示
  1. 2↵
  2. (())↵
  3. ()↵
以文本方式显示
  1. 1↵
1秒64M0

题意分析:

        简单题!

        上学期的程序设计基础课程里大家应该做过这道括号匹配的前身,然而这道题……我甚至觉得比那道还要简单。既然是两两匹配,所以我们只需要研究什么类型的括号序列和什么类型的能够匹配起来就行。

        首先,对于任何一个括号序列,如果我们能在中间找到某个“合法括号子串”,那么整个括号序列的匹配性质应该和将这个子串去掉后的括号序列是相同的,举个例子:对于序列“())()(()))”,容易发现左边有个子串“()”中间有个子串“()(())”都是合法括号序列,把这两个子串去掉,整个序列就变为了“))”,该序列与原序列的匹配性质应当是完全相同的,即对任意能匹配上这个序列的另一序列,一定能够与原序列匹配。

        按照上述方法,所有序列一定能被消解为以下四类之一:①只有左括号;②只有右括号;③为空;④既有右括号又有左括号,同时所有的右括号都在左括号左端。容易用反证法证明,除了以上四类的任何情况都不可能存在,因此我们只需要对以上四类进行匹配。

        对于④,没有任何可能只用一个括号序列就与它匹配,因为它的左右端都至少需要一个能够使得左端的右括号和右端的左括号同时闭合;对于③,任意一个其他③中的括号序列都可以与他匹配;对于①和②,当且仅当两个序列分属于两类,且括号数量相同的时候可以匹配。

        那么现在问题就变成了,如何把所有输入的括号序列全部都分到这四类里面去。当然,最简单的思路是和上个学期那道题一样,用栈存取序列主体,用一个overflow变量存左端溢出的右括号,逢左括号入栈,逢右括号若栈为空则overflow += 1,否则弹出栈顶元素。当然,考虑到这题只有单一的括号,不存在多种不同括号的匹配问题,所以这个栈的操作完全可以简化。具体流程与上述类似,只需要维护一个mainseq变量保存主体的左括号深度(即栈的大小),维护一个overflow变量保存左端溢出的右括号数量。如果最终二者同时大于0,则直接丢弃。若同时等于0,则为第三类。否则进入①②类,同时记得保存最后得到的括号深度(用map保存)。

        之后就是按照上面的方法来匹配了,没有其他有难度的地方。


伪代码:

        读入总数n;

        构建两个map<int, int> 用于保存①②类序列;

        初始化一个变量countClose= 0,用于记录③类序列数量;

        长为n的循环:

                初始化mainseqoverflow变量为0;

                逐个读入字符,直到回车符为止:

                        若为左括号,mainseq+=1

                        若为右括号,若mainseq=0overflow+=1,否则mainseq-=1

                若mainseq>0overflow>0,跳过本次循环;

                若mainseq>0overflow=0,表①中对应mainseq的值 += 1;

                //此处是c++特性,map<int, int>在未初始化时也可以直接调用,默认值为0,python的dict就不能这样用

                若mainseq=0overflow>0, 表②中对应mainseq的值 += 1;

                若二者同时为0,countClose += 1;

        初始化一个变量ans=0用于保存输出;

        ans += countClose // 2

        对表①的键k进行遍历,若该键出现在表②,则ans += min(map1[k], map2[k])

        输出ans

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千里之码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值