题意:给出一个只包含左括号和右括号的字符串,插入若干左右括号(可以插在任意位置)之后使得字符串长度为 2 n 2n 2n且是一个合法的括号序列。求最后能组成多少种不同的合法括号序列。【合法的括号序列:该序列任意一个前缀的左括号数大于等于右括号数,最终左括号数等于右括号数】。 n < = 100 n<=100 n<=100
三维 d p dp dp, d p dp dp的前两维分别为插入的括号数量和使用的原来的序列中的括号数量,而第三维是用来判断合法性的,所以第三维设为左括号比右括号多的数量。状态设计出来后,转移就比较好想了,设 m m m为读入的括号序列的长度。首先枚举插入的括号数量,原来的括号序列和左括号比右括号多的数量。首先如果目前枚举到的括号为左括号,并且使用的原括号的数量 < m <m <m,就可以将该括号放入最终序列中,即为: d p [ i ] [ j + 1 ] [ k + 1 ] + = d p [ i ] [ j ] [ k ] dp[i][j+1][k+1]+=dp[i][j][k]%mod+dp[i][j][k]%mod)%mod; dp[i][j+1][k+1]+=dp[i][j][k]
如果此时枚举到的是一个右括号,并且 k > 0 k>0 k>0,即左括号的数量大于右括号的数量,并且使用的原括号的数量 < m <m <m,就将该右括号放入最终序列,即为: d p [ i ] [ j + 1 ] [ k − 1 ] + = d p [ i ] [ j ] [ k ] dp[i][j+1][k-1]+=dp[i][j][k]%mod+dp[i][j][k]%mod)%mod; dp[i][j+1][k−1]+=dp[i][j][k]
如果使用的括号数量 = m =m =m,或当前枚举到的是一个右括号,可以插入一个左括号。即为: d p [ i + 1 ] [ j ] [ k + 1 ] + = d p [ i ] [ j ] [ k ] dp[i+1][j][k+1]+=dp[i][j][k]%mod+dp[i][j][k]%mod)%mod; dp[i+1][j][k+1]+=dp[i][j][k]
当 k > 0 k>0 k>0时,如果使用的括号数量 = m =m =m,或当前枚举到的是一个左括号,可以插入一个右括号。即为: d p [ i + 1 ] [ j ] [ k − 1 ] + = d p [ i ] [ j ] [ k ] dp[i+1][j][k-1]+=dp[i][j][k]%mod+dp[i][j][k]%mod)%mod; dp[i+1][j][k−1]+=dp[i][j][k]
答案就是 d p [ 2 ∗ n − m ] [ m ] [ 0 ] dp[2*n-m][m][0] dp[2∗n−m][m][0]
#include<bits/stdc++.h>
using namespace std;
int dp[220][220][220],n;
const int mod=1e9+7;
//dp[i][j][k]表示已经插入i个字符,使用原串j个字符且左括号比右括号多k个的方案数
char s[10000];
int main()
{
freopen("regular.in","r",stdin);
freopen("regular.out","w",stdout);
cin>>n;
scanf("%s",s);
int m=strlen(s);
dp[0][0][0]=1;
for(int i=0;i<=2*n-m;++i)
for(int j=0;j<=m;++j)
for(int k=0;k<=n;++k)
{
if(s[j]=='('&&j<m)
dp[i][j+1][k+1]=(dp[i][j+1][k+1]%mod+dp[i][j][k]%mod)%mod;
else
dp[i+1][j][k+1]=(dp[i+1][j][k+1]%mod+dp[i][j][k]%mod)%mod;
if(k>0)
{
if(s[j]==')'&&j<m)
dp[i][j+1][k-1]=(dp[i][j+1][k-1]%mod+dp[i][j][k]%mod)%mod;
else
dp[i+1][j][k-1]=(dp[i+1][j][k-1]%mod+dp[i][j][k]%mod)%mod;
}
}
cout<<dp[2*n-m][m][0];
return 0;
}