[机房测试]括号匹配
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
[](]]([))()][[[][(()]
*/