圆括号

圆括号

题目描述

慧音正在教妹红什么是合法括号序列:
空字符串是一个合法括号序列。
如果XY都是合法括号序列,那么XY(即字符串Y拼在字符串X的后面所组成的新的字符串)是一个合法括号序列。
如果X是合法括号序列,那么 (X) 也是合法括号序列(即在X的左边加一个左括号,右边加一个右括号)
所有的合法括号序列都可以用~的规则所构造出来。
现在妹红得到了两个括号序列(注意,不一定合法),记为XY,她想构造出一个新的括号序列Z。她每一次会将XY的最左边的字符加进Z的末尾,然后将其从XY中删去,直到XY都为空。她问慧音有多少种选择方案可以让Z是一个合法括号序列。两种选择方案不同是指至少存在一个位置k,使得第一种方案中Z的第k位从X得来,而在第二种方案中Z的第k位从Y得来(不是指两种方案的Z不同)。方案数mod 1000000007(10^9+7)
妹红觉得很好玩,于是她决定再随机生成偶数个括号序列,向慧音多次询问(即多组测试数据)。然而慧音并不知道怎么做,于是她交给了学OI的你。

输入格式 1989.in

第一行一个整数T,表示询问组数。
接下来每组询问有两行,每行一个字符串,分别代表题目中的XY

输出格式 1989.out

T行,每行一个整数,代表生成合法括号序列的方案数。

输入样例 1989.in

6
(()
())
(
)
(((((
)))))
()(()
))((())
()()()()()()()()()()()()()()()
()()()()()()()()
())())))
))((((

输出样例 1989.out

19
1
42
10
493841617
0

【样例说明】
第一组询问的19种方法:



红色代表来自X,蓝色代表来自Y
【数据范围】
T<=8XY长度不超过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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值