括号序列C++

题目:


样例解释:

如下几种方案是符合规范的:

(**)*()
(**(*))
(*(**))
(*)**()
(*)(**)

思路:

首先肯定是区间dp,令 dpi,jdpi,j​ 表示从位置 ii 到位置 jj 一共的合法序列总情况数量。

但是不同的形态可能会有不同的转移,如:(S)这种只能从S转移过来等等。所以只开两维的dp状态必然是不够的。

直接将方法吧。将两位的dp扩充为三维,第三位表示不同的形态种类,dp状态就变成了 dpi,j,[0,5]dpi,j,[0,5]​。没种状态表示:

  • dpi,j,0dpi,j,0​: 形态如***...*的括号序列(即全部是*)。

  • dpi,j,1dpi,j,1​: 形态如(...)的括号序列(即左右直接被括号包裹且最左边括号与最右边的括号相互匹配)。

  • dpi,j,2dpi,j,2​: 形态如(...)**(...)***的括号序列(即左边以括号序列开头,右边以*结尾)。

  • dpi,j,3dpi,j,3​: 形态如(...)***(...)*(...)的括号序列(即左边以括号序列开头,右边以括号序列结尾,注意:第2种形态也属于这种形态)。

  • dpi,j,4dpi,j,4​: 形态如***(...)**(...)的括号序列(即左边以*开头,右边以括号序列结尾)。

  • dpi,j,5dpi,j,5​: 形态如***(...)**(...)**的括号序列(即左边以*开头,右边以*结尾,注意:第1种形态也属于这种形态)。

设定完状态以后,转移就直接出来了,注意:为了防止连续超过 k个*一起出现,转移的时候不能把两段*拼接起来,在状态1的时候暴力判断一下两端的距离是否是 ≤k≤k 的,是的才能转移。

  • dpl,r,0dpl,r,0​(直接特判)

  • 。没什么好解释的
  • 。compare(i,j)compare(i,j) 表示第 i 位与第 j 位能否配对成括号,能则为 1,否则为 0。
  • 。加括号时,里面可以是全*,可以是有一边是*,也可以是两边都不是*,唯独不能两边都是*且中间有括号序列。
    • 左边以括号序列开头且以括号序列结尾的是第3种,右边接一串*,是第0种。
    • 左边以括号序列开头,结尾随便,符合的有第2和第3种,右边接一个括号序列,是第1种。
    • 记得加上直接一个括号序列的。
    • 左边以*开头,结尾随便,符合的有第4和第5种,右边接一个括号序列,是第1种。
    • 左边以*开头,以括号序列结尾,符合的是第4种,右边接一串*,是第0种。
    • 记得加上全是*的。

最后,答案必须以括号序列开头,以括号序列结尾,所以直接是 dp1,n,3dp1,n,3​。

这样,初始状态也就没什么问题了,对于所有的 ii 满足 1≤i≤n1≤i≤n,有 dpi,i−1,0=1dpi,i−1,0​=1 。

最终时间复杂度 O(6×n3) 不到,(后半部分填不满 n3 )。

 


代码:

#define int long long
#define mod 1000000007
int n,k,dp[510][510][6];
char s[510];
bool compare(int a,int b) {return (s[a]=='('||s[a]=='?')&&(s[b]==')'||s[b]=='?');}
signed main(){
    n=read(),k=read();
    scanf("%s",s+1);
    For(i,1,n) dp[i][i-1][0]=1;
    For(len,1,n){
        For(l,1,n-len+1){
            int r=l+len-1;
            if(len<=k) dp[l][r][0]=dp[l][r-1][0]&&(s[r]=='*'||s[r]=='?');
            if(len>=2){
                if(compare(l,r)) dp[l][r][1]=(dp[l+1][r-1][0]+dp[l+1][r-1][2]+dp[l+1][r-1][3]+dp[l+1][r-1][4])%mod;
                For(i,l,r-1){
                    dp[l][r][2]=(dp[l][r][2]+dp[l][i][3]*dp[i+1][r][0])%mod;
                    dp[l][r][3]=(dp[l][r][3]+(dp[l][i][2]+dp[l][i][3])*dp[i+1][r][1])%mod;
                    dp[l][r][4]=(dp[l][r][4]+(dp[l][i][4]+dp[l][i][5])*dp[i+1][r][1])%mod;
                    dp[l][r][5]=(dp[l][r][5]+dp[l][i][4]*dp[i+1][r][0])%mod;
                }
            }
            dp[l][r][5]=(dp[l][r][5]+dp[l][r][0])%mod;
            dp[l][r][3]=(dp[l][r][3]+dp[l][r][1])%mod;
        }
    }
    printf("%lld\n",dp[1][n][3]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值