圆括号
题目描述
慧音正在教妹红什么是合法括号序列:
①空字符串是一个合法括号序列。
②如果X和Y都是合法括号序列,那么XY(即字符串Y拼在字符串X的后面所组成的新的字符串)是一个合法括号序列。
③如果X是合法括号序列,那么 (X) 也是合法括号序列(即在X的左边加一个左括号,右边加一个右括号)
所有的合法括号序列都可以用①~③的规则所构造出来。
现在妹红得到了两个括号序列(注意,不一定合法),记为X和Y,她想构造出一个新的括号序列Z。她每一次会将X或Y的最左边的字符加进Z的末尾,然后将其从X或Y中删去,直到X与Y都为空。她问慧音有多少种选择方案可以让Z是一个合法括号序列。两种选择方案不同是指至少存在一个位置k,使得第一种方案中Z的第k位从X得来,而在第二种方案中Z的第k位从Y得来(不是指两种方案的Z不同)。方案数mod 1000000007(10^9+7)
妹红觉得很好玩,于是她决定再随机生成偶数个括号序列,向慧音多次询问(即多组测试数据)。然而慧音并不知道怎么做,于是她交给了学OI的你。
输入格式 1989.in
第一行一个整数T,表示询问组数。
接下来每组询问有两行,每行一个字符串,分别代表题目中的X和Y。
输出格式 1989.out
共T行,每行一个整数,代表生成合法括号序列的方案数。
输入样例 1989.in
6
(()
())
(
)
(((((
)))))
()(()
))((())
()()()()()()()()()()()()()()()
()()()()()()()()
())())))
))((((
输出样例 1989.out
19
1
42
10
493841617
0
【样例说明】
第一组询问的19种方法:
红色代表来自X,蓝色代表来自Y。
【数据范围】
T<=8,X,Y长度不超过50。
这一题,我是班里为数不多的没有做出来的人。
说说我的心路历程吧。看到这题,自然知道是DP。但是,我在判断状态合法上犯了难,我从构造合法括号序列的角度判断是否合法,把问题想得无比复杂。其实就是没有看清问题的本质——在除了最后之外的任意时刻,只要左括号的数量大于等于右括号,那么这就是个合法的序列。
至于为什么,其实很容易证明。如果右括号在前面找不到与之匹配的左括号,则必然非法。只要左括号能找到一个与之匹配的右括号,则必然合法。
搞清楚了判断合法性的问题,这道题就很简单了。根据不同方案的定义进行分析,状态、方程就可以轻易得出了。
f[i][j]表示X中选了i个字符,Y中选了j个字符组成合法括号序列的方案总数。在此状态为合法状态的情况下,最后一个字符要么是从X中来,要么是从Y中来,子问题就分别为f[i-1][j]和f[i][j-1]。因此,状态转移方程为:
f[i][j]=f[i-1][j]+f[i][j-1];
最后输出f[x.size()][y.size()]即可。
PS:对于问题,不要笨笨地被题目条件所迷惑,要深入地思考,挖掘题目信息,将所谓的复杂问题简单化。
代码如下:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int MAXN=55;
int t,a,b,len1,len2;
int sum1[MAXN][2],sum2[MAXN][2];
long long mod=1e9+7;
long long f[MAXN][MAXN],sum[MAXN][MAXN];
string x,y;
void init()
{
len1=x.size();
len2=y.size();
memset(f,0,sizeof(f));
memset(sum,0,sizeof(sum));
for(int i=0;i<len1;i++)
{
sum1[i+1][0]=sum1[i][0];
if(x[i]=='(') sum1[i+1][0]++;
}
for(int i=0;i<len2;i++)
{
sum2[i+1][0]=sum2[i][0];
if(y[i]=='(') sum2[i+1][0]++;
}
}
int main()
{
cin>>t;
while(t--)
{
cin>>x>>y;
init();
f[0][0]=1;
for(int i=0;i<=len1;i++)
{
for(int j=0;j<=len2;j++)
{
if(i==0&&j==0) continue;
a=sum1[i][0]+sum2[j][0];
b=i+j-a;
if(a<b) continue;
if(i==0) f[i][j]=f[i][j-1];
else if(j==0) f[i][j]=f[i-1][j];
else f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
}
}
if(a==b) cout<<f[len1][len2]<<endl;
else cout<<0<<endl;
}
return 0;
}