动态规划:括号序列

news/2024/11/28 17:54:01/

题目大意

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。

两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。

例如,对于括号序列 ( ( ( ) ((() (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果: ( ) ( ) ( ) ()()() ()()() ( ) ( ( ) ) ()(()) ()(()) ( ( ) ) ( ) (())() (())() ( ( ) ( ) ) (()()) (()()) ( ( ( ) ) ) ((())) ((()))

解题思路

s t e p 1 step1 step1

我们将左括号的值看作 1 1 1,右括号的值看作 − 1 -1 1

定义 s u m [ i ] sum[i] sum[i] 表示前 i i i 个括号的和, n n n 表示序列的长度,那么对于一个合法序列,其必定满足 ∀ i ∈ ( 1 , n ) , s u m [ i ] ≥ 0 \forall i\in(1,n),sum[i] \geq 0 i(1,n),sum[i]0 s u m [ n ] = 0 sum[n] = 0 sum[n]=0

s t e p 2 step2 step2

我们需要将添加括号使得原括号序列成为合法括号序列。
1、添加的左括号必然只能和原序列中的右括号匹配;
2、添加的右括号必然只能和原序列中的左括号匹配;
3、若添加的左括号和添加的右括号匹配了,则是一对无意义的添加。
⟺ \iff
1、添加的左括号的方案只和原序列中的右括号相关;
2、添加的右括号的方案只和原序列中的左括号相关,
3、以左右括号的添加是相互独立的。设左括号的添加方案为 f 1 f1 f1,右括号的添加方案为 f 2 f2 f2,那么显然最后的答案就是 f 1 × f 2 f1\times f2 f1×f2

s t e p 3 step3 step3

先考虑左括号的添加方案数。

设原序列中有 c n t cnt cnt 个右括号,我们以原序列中的右括号为分界点,那么一共可以划分出 c n t + 1 cnt+1 cnt+1 个区域。我们只能在第 1 ∼ c n t 1\sim cnt 1cnt 个区域添加左括号,因为若是在第 c n t + 1 cnt+1 cnt+1 区域添加左括号,将不会有右括号可以与它匹配。

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个区域(右括号),在我们添加了若干个左括号后一共有 j j j 个左括号的方案数。对于两个不同的方案,它们至少有一个区域添加的左括号的个数不相同,于是可得:

d p [ i ] [ j ] = d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] + ⋯ + d p [ i − 1 ] [ j ] dp[i][j] = dp[i - 1][0] + dp[i - 1][1] +\\ dp[i - 1][2] + \cdots+ dp[i - 1][j] dp[i][j]=dp[i1][0]+dp[i1][1]+dp[i1][2]++dp[i1][j]

注意

题干中要求合法序列,所以当右括号的个数为 i i i 时,它前面的左括号的个>数必然大于等于 i i i
我们定义 s u m [ i ] sum[i] sum[i] 表示第 i i i 个右括号之前的左括号个数,当 i > s u m [ i ] i > sum[i] i>sum[i] 时, j j j 必须满足 j ≥ i − s u m [ i ] j \geq i-sum[i] jisum[i]

添加左括号的方案数计算完成了。
对于右括号的添加方案,原括号序列反转,并令 ( → \rightarrow )) → \rightarrow (,然后按照求左括号的方案数的方法再求一遍即可。

优化——前缀和数组
对于 d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] + ⋯ + d p [ i − 1 ] [ j ] dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + \cdots+ dp[i - 1][j] dp[i1][0]+dp[i1][1]+dp[i1][2]++dp[i1][j],用别的式子来代替。

d p [ i ] [ j ] = d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + ⋯ + d p [ i − 1 ] [ j ] = ( d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + ⋯ + d p [ i − 1 ] [ j − 1 ] ) + d p [ i − 1 ] [ j ] = d p [ i ] [ j − 1 ] − d p [ i − 1 ] [ j ] dp[i][j] = dp[i - 1][0] + dp[i - 1][1] + \cdots +dp[i - 1][j] \\ =(dp[i - 1][0] + dp[i - 1][1] + \cdots + dp[i - 1][j-1] ) +dp[i - 1][j] = dp[i][j - 1] - dp[i - 1][j] dp[i][j]=dp[i1][0]+dp[i1][1]++dp[i1][j]=(dp[i1][0]+dp[i1][1]++dp[i1][j1])+dp[i1][j]=dp[i][j1]dp[i1][j]
j ∈ ( i − s u m [ i ] , c n t ) j\in(i-sum[i] , cnt) j(isum[i],cnt)
去掉一层循环,复杂度为 O ( n 2 ) O(n^2) O(n2)

仅当 j ∈ [ ( i − s u m [ i ] ) ∼ c n t ] j \in [(i-sum[i])\sim cnt] j[(isum[i])cnt] 时才满足 d p [ i ] [ j ] = ∑ k = 0 j d p [ i − 1 ] [ k ] dp[i][j] = \sum \limits_{k=0}^{j} dp[i - 1][k] dp[i][j]=k=0jdp[i1][k],所以 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j] = dp[i-1][j] + dp[i][j - 1] dp[i][j]=dp[i1][j]+dp[i][j1] 的转移并不完全合法。

用类似的方法来优化:

  • 定义 p r e [ j ] pre[j] pre[j] 表示 ∑ k = 1 j d p [ i − 1 ] [ k ] \sum\limits _{k=1}^{j}dp[i-1][k] k=1jdp[i1][k]
  • j ∈ [ 0 ∼ ( i − s u m [ i ] ) ] j \in[0\sim (i-sum[i])] j[0(isum[i])] 时, p r e [ j ] = 0 pre[j] = 0 pre[j]=0
  • j ∈ [ ( i − s u m [ i ] ) ∼ c n t ] j \in [(i-sum[i])\sim cnt] j[(isum[i])cnt] 时, p r e [ j ] = p r e [ j − 1 ] + d p [ i − 1 ] [ j ] pre[j] = pre[j - 1] + dp[i-1][j] pre[j]=pre[j1]+dp[i1][j]
  • 那么当 j ∈ [ ( i − s u m [ i ] ) ∼ c n t ] j \in [(i-sum[i])\sim cnt] j[(isum[i])cnt] d p [ i ] [ j ] = p r e [ j ] dp[i][j] = pre[j] dp[i][j]=pre[j]

这样时间复杂度就优化到 O ( n 2 ) O(n^2) O(n2) 了。

AC_Code

Python3


# -*- coding: utf-8 -*-
# @Author : BYW-yuwei
# @Software: python3.8.6MOD=int(1e9+7)def calc(cnt,flag,n):global MODif cnt==0:return 1dp=[[0]*(cnt+2) for i in range(n+5)]sum_res=[0]*(n+5)pre=[0]*(cnt+2) ; nex=[0]*(cnt+2)if flag==False:revstr=bracket_list[::-1]for i in range(0,n):if revstr[i]==')':bracket_list[i]='('else:bracket_list[i]=')'m,now=0,0for i in range(0,n):if bracket_list[i]==')':sum_res[m+1]=nowm+=1if bracket_list[i]=='(':now+=1if sum_res[1]>0:dp[1][0]=1 ; pre[0]=1for i in range(1,cnt+1):dp[1][i]=1pre[i]=pre[i-1]+1for i in range(2,m+1):dp[i]=prefor j in range(0,cnt+1):nex[j]=0for j in range(max(0,i-sum_res[i]),cnt+1):nex[j]=(nex[j-1]+dp[i][j])%MODpre=nex[:]return dp[m][cnt]if __name__=="__main__":bracket_list=[i for i in input()]n=len(bracket_list)tot,cnt1,cnt2=0,0,0for bracket in bracket_list:if bracket=='(':tot+=1else:tot-=1if tot<0:cnt1+=1tot=0cnt2=totres=calc(cnt1,1,n)*calc(cnt2,0,n)%MODprint(res)

http://www.ppmy.cn/news/559728.html

相关文章

Linux之tar安装

目录 Linux之tar安装 定义 工作过程 语法格式 参数及用法 使用源代码安装软件的优点 注意&#xff1a;源代码编译环境 操作流程 解包 —— tar 配置 —— ./configure 编译 —— make 安装 —— make install 案例 --- 安装Apache服务 1.获取安装包地址并下载 2…

内推专栏来了

金秋十月&#xff0c;信贷行业公司内推相关岗位如下&#xff1a; ->技术部&#xff1a;Java工程师、Web工程师、iOS工程师、python工程师、算法(nlp&#xff09;工程师、测试工程师。 ->风险管理部&#xff1a;风控策路分析师&#xff08;印尼复货、国内-AP1&复货&a…

阿里巴巴 淘特技术部 内推

阿里巴巴淘特技术部招人啦 部门直招&#xff0c;博主所在部门是供应链团队&#xff0c;团队直招Java开发&#xff0c;极其缺人我们喜欢基础扎实、对技术有热情的同学加入&#xff0c;可以一起迎接各种业务、产品、技术的挑战&#xff0c;目前P5、P6、P7、P8均开放大量职位。 如…

地推的作用是什么?

地推的作用是什么&#xff1f;商家为什么要我们帮忙地推。 地推在整个推广过程中起着媒人的作用&#xff0c;简单的说就是将产品介绍给用户。 地推是最接地气的方式&#xff0c;因为你直接接触到的就是你的用户&#xff0c;面对面的接触&#xff0c;最直接的交流。用户与你的互…

Twitter群推王教你一站式获客,轻松避坑

随着社交营销的火爆&#xff0c;越来越多的企业将目光转向了Twitter平台&#xff0c;这里Twitter群推王和大家分享一些小tips: 1、内容布置 用Twitter群推王&#xff0c;在Twitter上做营销的时候&#xff0c;需要先有一个规划&#xff0c;发哪些内容&#xff1f;内容和产品广…

推特群发需要细究的细节

推特群发现在太火了&#xff0c;无论是传统行业还是互联网行业都在用推特上进行营销&#xff0c;这里我们主要讲下推特发帖哪些容易漏掉的细节&#xff0c;以及以推特群推王为典型代表的推特群控&#xff08;推特营销机器人&#xff09;是如何进行引流的。 1、发布有图片的推文…

做推特群发群推“三不要怕”

1、不要害怕发广告。 如果你用很多的账号去推广&#xff0c;那么你完全不要顾忌&#xff0c;不要藏着掖着&#xff0c;大胆的告诉用户你的产品信息。因为对于没有需求的客户来说&#xff0c;就是不喜欢&#xff0c;没必要花大力气去迎合。你要用心去迎合有需求的客户。 那么问…

如何通过twitter群推王引流到listing

粉丝经济绝对是亚马逊卖家营销的最佳方式之一。这时卖家要想&#xff0c;我所选产品的目标客户是谁&#xff0c;他们需要什么&#xff1f;这样才能正确分享自己的资源&#xff0c;让粉丝觉得关注你的账号不是浪费时间和精力&#xff0c;而是真正对我有用。 保持在推特上发微博也…