[CSP-S 2021] 括号序列
括号序列
题面
给定一个长度为 ,包含 、、、 的字符串。
其中, 处字符串缺失,可能是 、、 中的任意一种。
定义超级括号序列如下:
-
和 均为符合规范超级括号序列,其中 表示由长度不超过 的 组成。
-
如果 和 为超级括号序列,那么 、 同样为符合规范的超级括号序列,其中 表示把 和 拼在一起形成的字符串。
-
如果 为符合规范的超级括号序列,那么 、、 均为符合规范的超级括号序列。
-
所有符合规范的超级括号序列均可以由以上 条规则得到。
求给出的字符串一共能对应多少种符合规范的超级括号序列。
给出 、 和一个长度为 的字符串,其中 。
分析
暴力
暴力分 分应该是直接搜索? ,期望能过前 个点。
区间
之后发现好像其他暴力不怎么会,注意到 的范围只有 ,我们需要一个 的算法来解决这个问题。
考虑一个区间 。
设 表示区间 到 的答案,之后,模拟上述的规则即可更新。
但之后你会发现你无法通过样例,思考这样 的正确性,发现我们算重了。
对于 ,我们也许会把它和后面一个匹配成功的 合并,但是我们也会将 合并,并在后面与 合并,考虑给我们的状态多加一维。
设 表示当前区间经历合并的方案数与没有经历合并的方案数,只让没有经历合并的方案和在它后面的经历过合并的方案合并,这样就能够避免这种情况。
一些细节
发现实现 是最后一个难关,很容易会让复杂度退化到 ,考虑设一个新的状态 表示该区间内 的方案数,之后可以与 的情况一起转移。
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e2+10,MOD=1e9+7;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,k;
int g[N][N],f[N][N],dp[N][N][2];
char arr[N];
inline bool check(int l,int r){
if(arr[l]=='('&&arr[r]==')') return true;
if(arr[l]=='('&&arr[r]=='?') return true;
if(arr[l]=='?'&&arr[r]==')') return true;
if(arr[l]=='?'&&arr[r]=='?') return true;
return false;
}
signed main()
{
// freopen("bracket.in","r",stdin);
// freopen("bracket.out","w",stdout);
n=read(),k=read();
cin>>arr+1;
for(int l=1;l<=n;l++){
if(arr[l]!='('&&arr[l]!='?') continue;
int cnt=0;
for(int r=l+1;r<=n;r++){
if(arr[r]==')'||arr[r]=='?') dp[l][r][0]=1;
if(arr[r]!='*'&&arr[r]!='?') break;
cnt++; if(cnt>k) break;
}
}
for(register int len=2;len<=n;len++){ //枚举区间长度
for(register int l=1;l<=n-len+1;l++){ //枚举左端点
int r=l+len-1; //右端点
int cnt=0;
for(register int x=l;x<r;x++){
if(arr[x]!='*'&&arr[x]!='?') break;
cnt++; if(cnt>k) break;
f[l][r]=(f[l][r]+(dp[x+1][r][0]+dp[x+1][r][1])%MOD)%MOD;
}
cnt=0;
for(register int x=r;x>l;x--){
if(arr[x]!='*'&&arr[x]!='?') break;
cnt++; if(cnt>k) break;
g[l][r]=(g[l][r]+(dp[l][x-1][0]+dp[l][x-1][1])%MOD)%MOD;
}
for(register int x=l;x<r;x++)
dp[l][r][1]=(dp[l][r][1]+dp[l][x][0]*((f[x+1][r]+(dp[x+1][r][0]+dp[x+1][r][1])%MOD)%MOD)%MOD)%MOD;
if(check(l,r)){ //端点可以为()
dp[l][r][0]=((dp[l][r][0]+dp[l+1][r-1][0])%MOD+dp[l+1][r-1][1])%MOD;
dp[l][r][0]=(dp[l][r][0]+f[l+1][r-1])%MOD;
dp[l][r][0]=(dp[l][r][0]+g[l+1][r-1])%MOD;
}
}
}
printf("%lld\n",(dp[1][n][0]+dp[1][n][1])%MOD);
return 0;
}
本文作者:╰⋛⋋⊱๑落叶๑⊰⋌⋚╯
本文链接:https://www.cnblogs.com/Defoliation-ldlh/p/15455662.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步