洛谷P7914 括号序列

题目描述

小 w 在赛场上遇到了这样一个题:一个长度为 nn 且符合规范的括号序列,其有些位置已经确定了,有些位置尚未确定,求这样的括号序列一共有多少个。

身经百战的小 w 当然一眼就秒了这题,不仅如此,他还觉得一场正式比赛出这么简单的模板题也太小儿科了,于是他把这题进行了加强之后顺手扔给了小 c。

具体而言,小 w 定义“超级括号序列”是由字符 ()* 组成的字符串,并且对于某个给定的常数 kk,给出了“符合规范的超级括号序列”的定义如下:

  1. ()(S) 均是符合规范的超级括号序列,其中 S 表示任意一个仅由不超过 \bm{k}k 字符 * 组成的非空字符串(以下两条规则中的 S 均为此含义);
  2. 如果字符串 A 和 B 均为符合规范的超级括号序列,那么字符串 ABASB 均为符合规范的超级括号序列,其中 AB 表示把字符串 A 和字符串 B 拼接在一起形成的字符串;
  3. 如果字符串 A 为符合规范的超级括号序列,那么字符串 (A)(SA)(AS) 均为符合规范的超级括号序列。
  4. 所有符合规范的超级括号序列均可通过上述 3 条规则得到。

例如,若 k = 3k=3,则字符串 ((**()*(*))*)(***) 是符合规范的超级括号序列,但字符串 *()(*()*)((**))*)(****(*)) 均不是。特别地,空字符串也不被视为符合规范的超级括号序列。

现在给出一个长度为 nn 的超级括号序列,其中有一些位置的字符已经确定,另外一些位置的字符尚未确定(用 ? 表示)。小 w 希望能计算出:有多少种将所有尚未确定的字符一一确定的方法,使得得到的字符串是一个符合规范的超级括号序列?

可怜的小 c 并不会做这道题,于是只好请求你来帮忙。

输入格式

第一行,两个正整数 n, kn,k。

第二行,一个长度为 nn 且仅由 ()*? 构成的字符串 SS。

输出格式

输出一个非负整数表示答案对 {10}^9 + 7109+7 取模的结果。

输入输出样例

输入 #1复制

7 3
(*??*??

输出 #1复制

5

输入 #2复制

10 2
???(*??(?)

输出 #2复制

19

【样例解释 #1】

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

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

【数据范围】

测试点编号n \len≤特殊性质
1 \sim 31∼31515
4 \sim 84∼84040
9 \sim 139∼13100100
14 \sim 1514∼15500500SS 串中仅含有字符 ?
16 \sim 2016∼20500500

对于 100 \%100% 的数据,1 \le k \le n \le 5001≤k≤n≤500。

上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rop(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define por(i,a,b) for(int i=(a);i>(b);i--)
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,k,S[502][502],A[502][502],_A[502][502],S_A[502][502];
char s[502];

bool isl(char c) {return c=='('||c=='?';}//判断该字符是否是/能填上(
bool isr(char c) {return c==')'||c=='?';}//判断该字符是否是/能填上)
bool isx(char c) {return c=='*'||c=='?';}//判断该字符是否是/能填上*

int main(){
	scanf("%lld%lld%s",&n,&k,s+1);
	rep(l,1,n){//预处理S[l][r]
		if(!isx(s[l])) continue;//不是*则枚举下一个l
		S[l][l]=1;
		rep(r,l+1,min(n,l+k-1)){//最多连续k个*,同时不能越界
			if(!isx(s[r])) break;//如果s[r]不是*,再往后就一定构不成连续的*
			S[l][r]=1;
		}
	}
	per(l,n-1,1){
		rep(r,l+1,n){
			if(r-l==1){//l与r连着,[l,r]只有作为()才能产生贡献
				if(isl(s[l])&&isr(s[r])) A[l][r]=_A[l][r]=1;//判断[l,r]是否为()
				continue;
			}
			if(isl(s[l])&&isr(s[r])){//判断外层()
				A[l][r]+=S[l+1][r-1],A[l][r]%=mod;//(S)
				A[l][r]+=A[l+1][r-1],A[l][r]%=mod;//(A)
				rop(i,l+1,r-1) A[l][r]+=S[l+1][i]*A[i+1][r-1]%mod,A[l][r]%=mod;//(SA)
				rop(i,l+1,r-1) A[l][r]+=A[l+1][i]*S[i+1][r-1]%mod,A[l][r]%=mod;//(AS)
			}
			_A[l][r]=A[l][r];//此时包含型情况的枚举完毕,_A[l][r]==此时的A[l][r]
			rop(i,l,r) S_A[l][r]+=S[l][i]*_A[i+1][r]%mod,S_A[l][r]%=mod;//维护S_A
			rop(i,l,r) A[l][r]+=A[l][i]*_A[i+1][r]%mod,A[l][r]%=mod;//A_A
			rop(i,l,r) A[l][r]+=A[l][i]*S_A[i+1][r]%mod,A[l][r]%=mod;//AS_A
		}
	}
	printf("%lld",A[1][n]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值