1.题目要求添加尽量少的括号,使之变成有效的括号序列 形如:(())
2.通过分析题意我们可以,大概模拟出三种括号的情况,如:((())--->左括号大于右括号且,(()))--->右括号大于左括号,))(((--->无一括号配对。
3.我们对其中的一种情况进行分析如:右括号大于左括号的情况 "(( )))",那么我们只需要添加左括号即可,通过在不同的右括号前添加 '(' 可以达到去重的效果
使用动态规划的做法就是dp[i][j],其中i代表第i个括号,j代表左括号比右括号多的个数,dp[i][j]代表第i个括号前左括号比右括号多j个的方案数,初始条件dp[0][0]=1
于是推出状态转移方程 当第i个字符为左括号时---->dp[i][j]=dp[i-1][j-1],
当第i个字符为右括号时dp[i][j]=dp[i-1][j+1]+dp[i-1][j]因为多添加了一个')'所以前i个字符中左括号因该比右括号多j+1个,+dp[i-1][j]
是因为如果我们在该 ')' 前添加一个 '(' 那么只需要dp[i-1][j]即可,因为抵消掉了一个 ')'
由于我们没办法判断题目输入会给出哪一种情况,于是我们把左括号和右括号的数量对换再算一次,假设第一次算出的是正确答案,第二次由于左括号始终大于右括号所以不断添加左括号也无法使其变成有效序列只能使原括号序列的情况为1
如是第三种情况 ))((( 相当于分开算使 ))变成合法序列的方案数乘以使(((变成合法括号序列的方案数,将 '(' ')' 的数量反转刚好可以求出各自的值
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const ll N=1e9+7;
char s[5001];
ll dp[5001][5001];
void dp_s(int len){
memset(dp,0,sizeof dp);
dp[0][0]=1;
for(int i=1;i<=len;i++){
if(s[i]=='('){
for(int j=1;j<=len;j++){
dp[i][j]=dp[i-1][j-1];
}
}else{
dp[i][0]=(dp[i-1][1]+dp[i-1][0])%N;//防止溢出
for(int j=1;j<=len;j++){
dp[i][j]=(dp[i-1][j+1]+dp[i][j-1])%N;
}
}
}
}
int main(){
scanf("%s",s+1);
int len=strlen(s+1);
ll ret1=0,ret2=0;
dp_s(len);
for(int i=0;i<=len;i++){
if(dp[len][i]){
ret1+=dp[len][i];//遇到第一个满足的就break
break;
}
}
int a=1,b=len;
while(a<b){
char temp=s[a];
s[a]=s[b];
s[b]=temp;
a++;
b--;
}
for(int i=1;i<=len;i++){
if(s[i]=='(')s[i]=')';
else s[i]='(';
}//将数量对换
dp_s(len);
for(int i=0;i<=len;i++){
if(dp[len][i]){
ret2+=dp[len][i];//遇到第一个满足的就break
break;
}
}
cout<<ret2*ret1%N;
return 0;
}