[机房测试]括号匹配

Description

给出长度为 \(n\) 的括号序列(只包含小括号和中括号),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。

Solution

写了三道题,只拍了这题,结果就这题没挂,真的服了。

考虑到一个合法的括号串是若干个单串拼接起来的,所以可以考虑通过这个来转移。\(f_{l,r}\) 表示区间 \([l,r]\) 的合法串个数,\(g_{l,r}\) 表示左端点是 \(l\),右端点在 \([l,r]\) 的合法单串个数。(为什么要保证左端点必须是 \(l\)?因为我们转移是通过枚举断点转移,钦定最后一段的端点就能保证不会算重)

\[f_{l,r}=\sum_{i=l}^r (f_{l,i-1}+1)\times g_{i,r} \\ g_{l,r}=\sum_{i=l+1}^r [S_l和S_i匹配] (f_{l+1,i-1}+1) \]

#include<stdio.h>
#include<string.h>

const int N=3e2+7;
const int Mod=1e9+7;

char s[N];
int g[N][N],f[N][N];

inline void Add(int &x,int y){
    x+=y; if(x>=Mod) x-=Mod;
}

inline int op(char x){
    if(x=='(') return 0;
    if(x=='[') return 1;
    if(x==']') return 2;
    if(x==')') return 3;
}

int Dfs(int,int);
int dfs(int,int);

int Dfs(int l,int r){
    if(l>=r) return 0;
    int &ret=g[l][r];
    if(~ret) return ret; else ret=0;
    if(op(s[l])>=2) return 0;
    for(int i=l+1;i<=r;i++)
        if(op(s[l])+op(s[i])==3) Add(ret,dfs(l+1,i-1)+1);
    return ret;
}

int dfs(int l,int r){
    if(l>=r) return 0;
    int &ret=f[l][r];
    if(~ret) return ret; else ret=0;
    for(int i=l;i<r;i++) Add(ret,Dfs(i,r));
    for(int i=l+1;i<=r;i++)
        Add(ret,1ll*dfs(l,i)*Dfs(i+1,r)%Mod);
    return ret;
}

int main(){
    freopen("parenthesis.in","r",stdin);
    freopen("parenthesis.out","w",stdout);
    int n; scanf("%d",&n);
    scanf("%s",s+1);
    memset(g,-1,sizeof(g));
    memset(f,-1,sizeof(f));
    printf("%d",dfs(1,n));
//    for(int l=1;l<=n;l++)
//        for(int r=l;r<=n;r++)
//            printf("[%d,%d] %d %d\n",l,r,dfs(l,r),Dfs(l,r));
}

/*
30
[(]))][(][((([[])()]])[([])))]

6 ()()[]

21
[](]]([))()][[[][(()]
*/
posted @ 2021-10-18 11:40  Kreap  阅读(37)  评论(0编辑  收藏  举报