社团活动每日一题:蓝桥杯2021省赛 括号序列

括号序列

蓝桥杯2021 省赛

题目描述

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。

两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。

例如,对于括号序列 ((()(((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()()()()、()(())()(())、(())()(())()、(()())(()()) 和 ((()))((()))。

输入描述

输入一行包含一个字符串 s,表示给定的括号序列,序列中只有左括号和右括号。

输出描述

输出一个整数表示答案,答案可能很大,请输出答案除以 10000000071000000007 (即 10^9 + 7)109+7) 的余数。

输入输出样例

示例 1

输入

((()

输出

5

评测用例规模与约定

对于 40%40 的评测用例,|s| \leq 200∣s∣≤200。

对于所有评测用例,1 \leq |s| \leq 50001≤∣s∣≤5000。

运行限制

  • 最大运行时间:1s

  • 最大运行内存: 256M

AC代码

#include <iostream>
#include <cstring>
using namespace std;
const long long Mod = 1000000007;
const int M = 5005;
long long dp[M][M];
int arr[M];
long long function(string s){
  memset(dp,0,sizeof(dp));      //初始化数组的函数,来自<cstring>或<string.h>库
  memset(arr,0,sizeof(arr));
  long long ans=0;
  int all,p=0;
  for(int i=0,t=0;i<s.length();i++){
    if(s[i]==')'){
      if(--t<0)p++;
      arr[p]++;
    }else if(t++<0)t=1;
  }
  dp[0][1] = 1,all = arr[0];
  for(int i=1;i<=p;i++){
    all += arr[i];
    for(int j=1;j<=all-arr[i]+1;j++){
      dp[i][j] = (dp[i][j-1]+dp[i-1][j]) % Mod;
    }
  }
  for(int i=1;i<=all-arr[p]+1;i++)ans = (ans+dp[p][i]) % Mod;
  return ans;
}
string fstr(string s){      //反转
  string t;
  for(int i=s.length()-1;i>=0;i--)t+= s[i]=='(' ? ')':'(';
  return t;
}
int main(){
  string s;
  cin>> s;
  cout<< (function(s) * function(fstr(s)))%Mod;
  return 0;
}

解析

这是一道“普通”难度的动态规划题,当我们第一次阅读完题目,发现像是暴力啊、递归啊什么的都不好用的时候,就要想到这是动态规划题了。

这道题和常见的动态规划题的难点一样:建立动态转移方程。分析完题目类型之后,可以从数据范围s<=5000粗略地推出来我们的dp应该是个二维数组。

简要分析题目,不难看出补充左括号'('和补充右括号')'的规则一致,无非是从左往右添加和从右往左添加的区别,所以笔者这里偷了点懒,按照正常顺序计算出左括号添加的方案数后,将字符串的顺序以及左右括号反转,按照同一套规则找出了有括号的添加方案数。不难推出我们所需要的答案就是 左括号方案数*右括号方案数%Mod 的值。

下面是笔者建立的动态转移方程:

dp[i][j] = dp[i][j-1]+dp[i-1][j]; 
i : 需要添加的第 i 个左括号 
j : 在第 j 个右括号的左侧

然后是建立这个动态转移方程的思路:

  • 从左向右遍历,如果出现'(',将临时存储的t值+1(初始是0),遇到')'则-1,当t<0时,我们就知道我们需要补一个'('了。当然,在t<0的情况下遇到'('的时候要将t清零再+1(可以有多种方式实现这种效果比如t=0的时候不再减小t的值)。

  • 因为我们以')'为隔板,隔板间'('的数量其实不会影响到我们的排列组合,所以笔者使用了arr数组来存储每个需要补括号的位置及它的右侧一共有多少个')',这里也有多种方式可以实现。

  • 当我们尝试去计算第 i 个'('放在第 j 个')'左边有多少种可行方法时,会发现一共只有两种方案:一种是添加在第 j 个位置,其方案数等同于第 i-1 个'('添加在同一位置的方案数,也就是 dp[i-1][j];另一种是不选择在第 j 个位置添加任何'(',其方案数等同于在上一个位置安放'('的方案数,也就是dp[i][j-1]

  • 不要忘了每次计算去与Mod取模以及一些小细节。

吐槽

太久没做动态规划了写了一下午这道题,人都写麻了QAQ

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值