CSP-S2021提高组第二轮T2:括号序列

题目链接

[CSP-S 2021] 括号序列

题目描述

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

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

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

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

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

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

7 3
(*??*??

样例输出 #1

5

样例 #2

样例输入 #2

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

样例输出 #2

19

提示

【样例解释 #1】

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

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

【数据范围】

测试点编号 n ≤ n \le n特殊性质
1 ∼ 3 1 \sim 3 13 15 15 15
4 ∼ 8 4 \sim 8 48 40 40 40
9 ∼ 13 9 \sim 13 913 100 100 100
14 ∼ 15 14 \sim 15 1415 500 500 500 S S S 串中仅含有字符 ?
16 ∼ 20 16 \sim 20 1620 500 500 500

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

算法思想

根据题目描述,符合规范的超级括号序列有下面几种情况:

  • ()
  • (S),将S加上一层括号是符合规范的。其中 S 表示任意一个仅由不超过 k \bm{k} k 字符 * 组成的非空字符串;
  • (A)表示对一个符合规范的序列再加一层括号也符合规范的
  • (SA)(AS)表示在一个符合规范的序列左边(或右边)连接一个S再加一层括号也是符合规范的
  • AB表示并排在一起的符合规范的序列也是合法的
  • ASB表示将S放在并排在一起的符合规范的序列之中也是合法的

k = 3 k = 3 k=3,则字符串 ((**()*(*))*)(***)是否合法可以这样分析:

  • 字符串的右边(***)是一个合法的序列(S),如果左边的子串((**()*(*))*)是一个合法序列,则并排在一起符合规范AB
  • ((**()*(*))*)如果其中的子串(**()*(*))是一个合法序列,则符合规范(AS)
  • (**()*(*))如果其中的子串()*(*)是一个合法序列,则符合规范(SA)
  • ()*(*)符合规范ASB,是一个合法序列

因此整个序列是符合规范的超级括号序列。

也就是说,对于字符串中任意一个区间 [ i , j ] [i,j] [i,j]判断是否符合规范,则需要判断其子区间是否符合规范,那么可以使用区间动态规划来处理。

状态表示

f [ i ] [ j ] f[i][j] f[i][j]表示将字符串中从 i i i j j j位置的字符确定后,得到的字符串是一个符合规范的超级括号序列的方案数。由于从 i i i j j j位置的字符可能是其中的一个子串,因此存在多种合法的状态:

  1. f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]表示由单独的符合规范的超级括号序列组成的方案数,形式如()(S)(A)(SA)(AS)
  2. f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示由并排的符合规范的超级括号序列组成的方案数,形式如ABASB
  3. f [ i ] [ j ] [ 2 ] f[i][j][2] f[i][j][2]表示仅由不超过 k \bm{k} k 字符 * 组成的方案数,形式如***…。需要注意的是只有 * 组成的子串无法单独构成符合规范的超级括号序列。
  4. f [ i ] [ j ] [ 3 ] f[i][j][3] f[i][j][3]表示由符合规范的超级括号序列+不超过 k \bm{k} k * 组成的方案数,形式如AS。该方案也无法单独构成符合规范的超级括号序列。
  5. f [ i ] [ j ] [ 4 ] f[i][j][4] f[i][j][4]表示由不超过 k \bm{k} k * + 符合规范的超级括号序列组成的方案数,形式如SA。该方案也无法单独构成符合规范的超级括号序列。

最终的方案总数为 f [ 1 ] [ n ] [ 0 ] + f [ 1 ] [ n ] [ 1 ] f[1][n][0]+f[1][n][1] f[1][n][0]+f[1][n][1], 其中 n n n表示字符串 S S S的长度。

状态计算

  • f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]表示由单独的符合规范的超级括号序列组成的方案数,那么去掉 i i i j j j位置的括号后,子串的形式可以是空串AABSSAAS的任意一种情况,因此, f [ i ] [ j ] [ 0 ] = f [ i + 1 ] [ j − 1 ] [ 0 ] + f [ i + 1 ] [ j − 1 ] [ 1 ] + f [ i + 1 ] [ j − 1 ] [ 2 ] + f [ i + 1 ] [ j − 1 ] [ 3 ] + f [ i + 1 ] [ j − 1 ] [ 4 ] f[i][j][0]=f[i+1][j-1][0]+f[i+1][j-1][1]+f[i+1][j-1][2]+f[i+1][j-1][3]+f[i+1][j-1][4] f[i][j][0]=f[i+1][j1][0]+f[i+1][j1][1]+f[i+1][j1][2]+f[i+1][j1][3]+f[i+1][j1][4]

    注意:当长度为 2 2 2时,也就是只有括号的情况下,子串为空, f [ i ] [ j ] [ 0 ] = 1 f[i][j][0] = 1 f[i][j][0]=1

  • f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示由并排的符合规范的超级括号序列组成的方案数,如果以一个符合规范的超级括号序列B进行分类,那么子串的形式可以是AAS。因此可以枚举最后一个B和前面的分界点 k k k f [ i ] [ j ] [ 1 ] = ∑ k = i j − 1 ( f [ i ] [ k ] [ 0 ] + f [ i ] [ k ] [ 1 ] + f [ i ] [ k ] [ 3 ] ) × f [ k + 1 ] [ j ] [ 0 ] f[i][j][1]=\sum_{k=i}^{j-1}(f[i][k][0]+f[i][k][1]+f[i][k][3])\times f[k+1][j][0] f[i][j][1]=k=ij1(f[i][k][0]+f[i][k][1]+f[i][k][3])×f[k+1][j][0]

  • f [ i ] [ j ] [ 2 ] f[i][j][2] f[i][j][2]表示仅由 * 组成的方案数,只能从状态 [ 2 ] [2] [2]转移过来,所以 f [ i ] [ j ] [ 2 ] = f [ i + 1 ] [ j ] [ 2 ] f[i][j][2]=f[i+1][j][2] f[i][j][2]=f[i+1][j][2]

    注意:当长度为 1 1 1时,且只有*的情况下, f [ i ] [ j ] [ 2 ] = 1 f[i][j][2] = 1 f[i][j][2]=1

  • f [ i ] [ j ] [ 3 ] f[i][j][3] f[i][j][3]表示由符合规范的超级括号序列+* 组成的方案数,以后面的S进行分类,前面的子串形式可以是AAB,因此可以枚举S和前面的分界点 k k k f [ i ] [ j ] [ 3 ] = ∑ k = i j − 1 ( f [ i ] [ k ] [ 0 ] + f [ i ] [ k ] [ 1 ] ) × f [ k + 1 ] [ j ] [ 2 ] f[i][j][3]=\sum_{k=i}^{j-1}(f[i][k][0]+f[i][k][1]) \times f[k+1][j][2] f[i][j][3]=k=ij1(f[i][k][0]+f[i][k][1])×f[k+1][j][2]

  • f [ i ] [ j ] [ 4 ] f[i][j][4] f[i][j][4]表示由* + 符合规范的超级括号序列组成的方案数,以前面的S进行分类,前面的子串形式可以是AAB,因此可以枚举S和后面的分界点 k k k f [ i ] [ j ] [ 4 ] = ∑ k = i j − 1 f [ i ] [ k ] [ 2 ] × ( f [ k + 1 ] [ j ] [ 0 ] + f [ k + 1 ] [ j ] [ 1 ] ) f[i][j][4]=\sum_{k=i}^{j-1} f[i][k][2] \times (f[k+1][j][0]+f[k+1][j][1]) f[i][j][4]=k=ij1f[i][k][2]×(f[k+1][j][0]+f[k+1][j][1])

时间复杂度

  • 状态数为 n × n × 5 n\times n\times5 n×n×5
  • 状态计算需要枚举分界位置,时间复杂度最大为 O ( n ) O(n) O(n)

总的时间复杂度为 O ( 5 × n 3 ) = 625 , 000 , 000 O(5\times n^3)=625,000,000 O(5×n3)=625,000,000,由于枚举区间起点和分界位置的时间复杂度要低于 O ( n ) O(n) O(n),刚好可以AC。

代码实现

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 510, MOD = 1e9 + 7;
char s[N];
/*
f[i][j][0]表示由单独的符合规范的序列组成的方案数
f[i][j][1]表示由并排的符合规范的序列组成的方案数
f[i][j][2]表示仅由*组成的方案数
f[i][j][3]表示由符合规范的序列 + *组成的方案数
f[i][j][4]表示由* + 符合规范的序列组成的方案数
*/
int f[N][N][5];
//判断a和b是否匹配
bool is_match(char a, char b)
{return a == b || a == '?';
}
int main()
{int n, m;cin >> n >> m;cin >> s + 1; //字符串下标从1开始//枚举区间长度for(int len = 1; len <= n; len ++){//枚举区间开始位置for(int i = 1; i + len - 1 <= n; i ++){int j = i + len - 1; //结束位置if(len == 1) //区间长度为1{//只有1个星号if(is_match(s[i], '*')) f[i][j][2] = 1;}else{//计算状态[0],由单独的符合规范的序列组成的方案数if(is_match(s[i], '(') && is_match(s[j], ')')){//当长度为2时,也就是只有括号的情况下,子串为空if(len == 2) f[i][j][0] = 1;//[0] = [0] + [1] + [2] + [3] + [4]for(int k = 0; k < 5; k ++)f[i][j][0] = (f[i][j][0] + f[i + 1][j - 1][k]) % MOD;}//计算状态[2],仅由*组成的方案数if(len <= m && is_match(s[i], '*'))f[i][j][2] = f[i + 1][j][2];//枚举分界位置,计算其它状态[1]、[3]、[4]for(int k = i; k < j; k ++){f[i][j][1] = (f[i][j][1] + (f[i][k][0] + (LL)f[i][k][1] + f[i][k][3]) * f[k + 1][j][0]) % MOD;f[i][j][3] = (f[i][j][3] + (f[i][k][0] + (LL)f[i][k][1]) * f[k + 1][j][2]) % MOD;f[i][j][4] = (f[i][j][4] + f[i][k][2] * ((LL)f[k + 1][j][0] + f[k + 1][j][1])) % MOD;}}}}cout << (f[1][n][0] + f[1][n][1]) % MOD;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无人机覆盖路径规划综述

摘要&#xff1a;覆盖路径规划包括找到覆盖某个目标区域的每个点的路线。近年来&#xff0c;无人机已被应用于涉及地形覆盖的多个应用领域&#xff0c;如监视、智能农业、摄影测量、灾害管理、民事安全和野火跟踪等。本文旨在探索和分析文献中与覆盖路径规划问题中使用的不同方…

好用的chatgpt工具用过这个比较快

chatgpthttps://www.askchat.ai?r237422 chatGPT能做什么 1. 对话和聊天&#xff1a;我可以与您进行对话和聊天&#xff0c;回答您的问题、提供信息和建议。 2. 问题回答&#xff1a;无论是关于事实、历史、科学、文化、地理还是其他领域的问题&#xff0c;我都可以尽力回答…

php时间和centos时间不一致

PHP 时间和 CentOS 操作系统时间不一致的问题通常是由于时区设置不同造成的。解决这个问题可以通过以下几个步骤&#xff1a; 检查 CentOS 系统时间&#xff1a; 你可以通过在终端运行命令 date 来查看当前的系统时间和时区。 配置 CentOS 的时区&#xff1a; 如果系统时间不正…

关于前端学习的思考-内边距、边框和外边距

从最简单的盒子开始思考 先把实际应用摆出来&#xff1a; margin&#xff1a;居中&#xff0c;控制边距。 padding&#xff1a;控制边距。 border&#xff1a;制作三角形。 盒子分为内容盒子&#xff0c;内边距盒子&#xff0c;边框和外边距。 如果想让块级元素居中&#…

【排序】希尔排序(C语言实现)

文章目录 前言1. 希尔排序的思想2. 希尔排序的一些小优化 前言 本章将详细介绍希尔排序的思想及实现&#xff0c;由于希尔排序是在插入排序的思想上进行升华&#xff0c;所以如果不知道插入排序或者不熟悉的可以先看看这篇文章&#xff1a;《简单排序》中的直接插入排序。 1. 希…

《golang设计模式》第三部分·行为型模式-09-策略模式(Strategy)

文章目录 1. 概述1.1 作用1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 1.1 作用 策略&#xff08;Strategy&#xff09;是用于封装一组算法中单个算法的对象&#xff0c;这些策略可以相互替换&#xff0c;使得单个算法的变化不影响使用它的客户端。 1.1 …

软件设计师——程序设计语言基础(一)

&#x1f4d1;前言 本文主要是【程序设计语言基础】——程序设计语言基础的相关题目&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#…

TA-Lib学习研究笔记——Overlap Studies(二)上

TA-Lib学习研究笔记——Overlap Studies&#xff08;二&#xff09; 1. Overlap Studies 指标 [BBANDS, DEMA, EMA, HT_TRENDLINE, KAMA, MA, MAMA, MAVP, MIDPOINT, MIDPRICE, SAR, SAREXT, SMA, T3, TEMA, TRIMA, WMA]2.数据准备 get_data函数参数&#xff08;代码&#x…

UI自动化测试的正确姿势 —— Airtest设备连接API详解第一篇

一、背景 Airtest作为一款优秀的自动化测试工具&#xff0c;有着强大的API功能&#xff0c;处理日常自动化测试过程中需要的各类操作。今天就给大家逐一介绍关于设备连接和常用API部分&#xff0c;结合自动化测试中的各类需求&#xff0c;看看如何通过使用Airtest来快速实现。…

leetcode 15. 三数之和(优质解法)

代码&#xff1a; class Solution {public static List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);List<List<Integer>> listsnew ArrayList<>();int lengthnums.length;for(int i0;i<length-3;){int lefti1;int rightlength…

【论文学习】机器学习模型安全与隐私研究综述

机器学习在数据层、模型层以及应用层面临的安全和隐私威胁&#xff0c;呈现出多样性、隐蔽性和动态演化的特点。 应用领域&#xff1a;计算机视觉、自然语言处理、语音识别等 应用场景&#xff1a;自动驾驶、人脸识别、智慧医疗等 Key words: machine learning; poisoning atta…

设计模式之Strategy策略模式

Strategy策略模式 我的理解是在不同情况下使用不同方法&#xff0c;而方法可以增加&#xff0c;所以写一个方法父类&#xff0c;让所有方法继承下来&#xff0c;在子类写实现&#xff0c;添加时写新的子类就可以 动机 在软件构建过程中&#xff0c;某些对象使用的算法可能多…

【星火大模型】api使用

讯飞星火官方首页 准备工作 注册讯飞星火账号申请开发者api试用 从一个demo开始 讯飞星火官方的程序员为我们提供了非常优秀的demo&#xff0c;基本涵盖了大多数常用语言。 demo下载链接 这里我选用Java带上下文调用示例 下载后可以看到这是一个idea项目&#xff0c;直接…

2336. 无限集中的最小数字 : 容易又高效的分类做法

题目描述 这是 LeetCode 上的 「2336. 无限集中的最小数字」 &#xff0c;难度为 「中等」。 Tag : 「优先队列&#xff08;堆&#xff09;」、「哈希表」 现有一个包含所有正整数的集合 。 实现 SmallestInfiniteSet 类&#xff1a; SmallestInfiniteSet() 初始化 SmallestIn…

Python实现学生信息管理系统(详解版)

Python实现学生信息管理系统-详解版 个人简介实验名称&#xff1a;学生信息管理系统系统功能实验步骤详讲添加入住学生信息删除学生的住宿信息修改学生的住宿信息查询学生的住宿信息显示所有学生住宿信息显示所有请假学生的信息 运行截图展示1.主界面2.添加新的入住学生信息3.显…

Django路由分发

首先明白一点&#xff0c;Django的每一个应用下都可以有自己的templates文件夹&#xff0c;urls.py文件夹&#xff0c;static文件夹&#xff0c;基于这个特点&#xff0c;Django能够很好的做到分组开发&#xff08;每个人只写自己的app&#xff09;&#xff0c;作为老大&#x…

如何自定义spring-boot-starter

1. 创建自定义starter 1.1 生成Maven工程 mvn archetype:generate -DarchetypeGroupIdorg.apache.maven.archetypes -DarchetypeArtifactIdmaven-archetype-quickstart -DarchetypeVersion1.4交互式输入groupId、artificatId、version&#xff0c;生成Maven工程后用IDEA打开 …

详细学习Pyqt5中的2种弹簧

Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图&#xff08;Item View&#xff09; 快速弄懂Pyqt5的4种项目部件&#xff08;Item Widget&#xff09; 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…

第十三章 python之爬虫

Python基础、函数、模块、面向对象、网络和并发编程、数据库和缓存、 前端、django、Flask、tornado、api、git、爬虫、算法和数据结构、Linux、设计题、客观题、其他 第十三章 爬虫 1. 写出在网络爬取过程中, 遇到防爬问题的解决办法。 在网络爬取过程中&#xff0c;可能会遇…

工业物联网数据传输方式探究

文章目录 引言Modbus RTU&#xff1a;传统数据采集的基础Modbus TCP&#xff1a;现代工业物联网的关键演进与影响 结语 引言 工业物联网的发展为工业数据采集带来了巨大的变革。从最初的 Modbus RTU 到现在广泛应用的 Modbus TCP&#xff0c;数据采集方式的演进使得工业领域的…