NC18413 括号 动态规划

链接:https://ac.nowcoder.com/acm/problem/18413
来源:牛客网

题目描述

小A有一个只包含左右括号的字符串S。但他觉得这个字符串不够美观,因为它不是一个合法的括号串。一个合法的括号串是这样定义的:
1. ()是合法的括号串
2. 若A是合法的括号串,则(A)则是合法的括号串
3. 若A,B是合法的括号串,则AB也是合法的括号串。

小A现在希望删掉S中若干个字符,使得剩下的字符串是一个合法的括号串。小A想知道有多少不同的方案。两个方案是不同的,当且仅当他们删除的位置不同。比如当S是(()时,有两种方案。分别是删掉第一个位置,或是删掉第二个位置。

输入描述:

第一行一个整数n,代表S的长度。
第二行输入n个字符,字符要么是(,要么是)。代表串S。

输出描述:

一行一个整数,代表不同的方案数。答案对10^9+7取模。
示例1

输入

复制
8
)(()(())

输出

复制
30

备注:

20%: n <= 20
40%: n <= 100
60%: n <= 1000
100%: n <= 10000

分析

首先,得确定是哪种DP,这题是计算完全匹配的方案数量,不是选择。但可以是区间DP,或者线性DP。

为什么不是区间DP,而是线性DP?

这道题的数据范围是1e5,n^2 显然不可能,那就只能是线性DP了

线性DP,随着字符串长度的变长,完全匹配的方案是在增加的,不匹配的左括号也是在增加的,不匹配的左括号的数目最多是多少呢?是当前遍历到的字符串长度。什么时候完全匹配的方案会增加呢?遇到右括号的时候。什么时候不匹配的左括号会增加呢?遇到左括号的时候。

假设f[i][j] 表示 遍历到第i个字符,总共有j个不匹配的左括号的方案数。

由于还没遍历数组的时候,是空集,方案合法,所以初值:f[0][0] = 1 

当遍历到左括号,不匹配的方案数增加,容易想到状态转移方程式:f[i][j] += f[i-1][j-1]

当遍历到右括号,匹配的方案数增加,容易想到状态转移方程是:f[i][j] += f[i-1][j+1]

更改遍历顺序能够压缩数组成一维:f[j] 表示 遍历到第i个字符,总共有j个不匹配的左括号的方案数

最后输出答案f[0] - 1(去掉空集,由例子知,该方案不合法)

代码

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 10005;
const ll mod = 1e9 + 7;
ll f[N]; //表示前i个字符中有j个左括号未配对成功的方案数
int n;
char s[N];

int main()
{
scanf("%d%s",&n,s+1);
f[0] = 1;
for(int i = 1;i<=n;i++) {
if(s[i] == '(') {
for(int j = n;j>=0;j--) {
if(j !=0 ) f[j] = (f[j] + f[j-1]) % mod;
}
} else {
for(int j = 0;j<=n-1;j++) {
f[j] = (f[j] + f[j+1]) % mod;
}
}
}
f[0] -- ;
cout<<f[0]<<endl;
}

posted @ 2022-07-10 00:16  er007  阅读(63)  评论(0编辑  收藏  举报