2021-10-24 20:59阅读: 433评论: 4推荐: 5

[CSP-S 2021] 括号序列

括号序列

题面

给定一个长度为 n ,包含 ?() 的字符串。

其中, ? 处字符串缺失,可能是 () 中的任意一种。

定义超级括号序列如下:

  • ()(S) 均为符合规范超级括号序列,其中 S 表示由长度不超过 k 组成。

  • 如果 AB 为超级括号序列,那么 ABASB 同样为符合规范的超级括号序列,其中 AB 表示把 AB 拼在一起形成的字符串。

  • 如果 A 为符合规范的超级括号序列,那么 (A)(SA)(AS) 均为符合规范的超级括号序列。

  • 所有符合规范的超级括号序列均可以由以上 3 条规则得到。

求给出的字符串一共能对应多少种符合规范的超级括号序列。

给出 nk 和一个长度为 n 的字符串,其中 1kn500

分析

暴力

暴力分 15 分应该是直接搜索?O(3n) ,期望能过前 3 个点。

区间 DP

之后发现好像其他暴力不怎么会,注意到 n 的范围只有 500 ,我们需要一个 O(n3) 的算法来解决这个问题。

考虑一个区间 DP

dp[l][r] 表示区间 lr 的答案,之后,模拟上述的规则即可更新。

但之后你会发现你无法通过样例,思考这样 dp 的正确性,发现我们算重了。

对于 A ,我们也许会把它和后面一个匹配成功的 BC 合并,但是我们也会将 AB 合并,并在后面与 C 合并,考虑给我们的状态多加一维。

dp[l][r][0/1] 表示当前区间经历合并的方案数与没有经历合并的方案数,只让没有经历合并的方案和在它后面的经历过合并的方案合并,这样就能够避免这种情况。

一些细节

发现实现 ASB 是最后一个难关,很容易会让复杂度退化到 O(n4) ,考虑设一个新的状态 f[l][r] 表示该区间内 SB 的方案数,之后可以与 AB 的情况一起转移。

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 中国大陆许可协议进行许可。

posted @   ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(433)  评论(4编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起