括号序列
蓝桥杯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