0
点赞
收藏
分享

微信扫一扫

试题I 括号序列

题目链接:https://www.lanqiao.cn/courses/2786/learning/?id=280825

题目解析:

这是一道动态规划题
合法的括号序列满足以下两个条件

①从左往右数,任意一个位置的左括号数量均不少于右括号

②从右往左数,任意一个位置的右括号数量均不少于左括号

②也可表表述为:将整个括号序列翻转,并将左括号视作右括号,右括号视作左括号时,①仍然成立

下面来谈谈①的实现:

考虑到n个左括号的全排列只有一种,因此我们约定,仅在右括号的前面添加左括号

显然最多添加左括号的数量为整个序列长度

dp[i][j]表示在第i个位置上(算上第i个位置),左括号比右括号多j个的方案数

则状态转移方程为

dp[i][j]=dp[i-1][0]+dp[i-1][1]+...+dp[i-1][j]+dp[i-1][j+1]

当j >= 1时,注意到:

dp[i][j-1]=dp[i-1][0]+dp[i-1][j]+...+dp[i-1][j]

综合上述两式,即可得到:

dp[i][j]=dp[i][j-1]+dp[i-1][j+1](j>=1)

dp[i][0]=dp[i-1][0]+dp[i-1][1](j==0)

后在最后一个位置看dp[i][j]>0&&j取最小值,即最后一个位置上左括号比右括号多的数量取最小值的方案数,即满足①

翻转后再进行一次①,得到另外一种方案

两种方案相乘(取模),即为结果。

代码:

//#define local
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
#define MAXN 5005
const int mod=1e9+7; 
typedef long long ll;
using namespace std;
char str[MAXN];
ll dp[MAXN][MAXN]={0};
int main(){
#ifdef local
freopen("data.in","rb",stdin);
//freopen("data.out","wb",stdout);
#endif
scanf("%s",str+1);
int len=strlen(str+1);
dp[0][0]=1;
for(int i=1;i<=len;++i){
	for(int j=0;j<=len;++j){
		if(str[i]=='('&&j!=0)
		dp[i][j]=dp[i-1][j-1];
		else if(str[i]==')'){
			if(j==0)
			dp[i][j]=(dp[i-1][0]+dp[i-1][1])%mod;
			else
			dp[i][j]=(dp[i][j-1]+dp[i-1][j+1])%mod;
		}
	}
}
ll left=0;
for(int j=0;j<=len;++j){
	if(dp[len][j]>0){
		left=(dp[len][j])%mod;
		break;
	}
}
reverse(str+1,str+1+len);
fill(dp[0],dp[0]+MAXN*MAXN,0);
dp[0][0]=1;
for(int i=1;i<=len;++i){
	for(int j=0;j<=len;++j){
		if(str[i]==')'&&j!=0)
		dp[i][j]=dp[i-1][j-1];
		else if(str[i]=='('){
			if(j==0)
			dp[i][j]=(dp[i-1][0]+dp[i-1][1])%mod;
			else
			dp[i][j]=(dp[i][j-1]+dp[i-1][j+1])%mod;
		}
	}
}
ll right=0;
for(int j=0;j<=len;++j){
	if(dp[len][j]>0){
		right=(dp[len][j])%mod;
		break;
	}
}
printf("%lld",left*right%mod);
return 0;
} 
举报

相关推荐

0 条评论