mirror of
https://gitcode.com/Zengtudor/alg2025.git
synced 2025-10-17 21:42:25 +00:00
feat: 添加超级括号序列问题的动态规划解法
实现基于区间DP的解决方案,处理四种不同状态转移: 1. 任意合法序列的计数 2. 前缀星号序列的计数 3. 被括号包裹序列的计数 4. 后缀星号序列的计数 包含预处理步骤快速判断子串是否可全为星号,使用模运算防止溢出。
This commit is contained in:
parent
66f957900f
commit
929a5ed188
126
src/9/25/P7914.cpp
Normal file
126
src/9/25/P7914.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 使用 long long 防止中间结果溢出
|
||||
using ll = long long;
|
||||
|
||||
const int MOD = 1e9 + 7;
|
||||
|
||||
// DP 状态数组
|
||||
ll f[505][505]; // f[i][j]: S[i..j] 构成一个任意合法超级括号序列的方案数
|
||||
ll g[505][505]; // g[i][j]: S[i..j] 构成 "SA" 形式(S 为前缀 * 串)的方案数
|
||||
ll h[505][505]; // h[i][j]: S[i..j] 构成 A 类(被括号包裹)序列的方案数
|
||||
ll f_suff_S[505][505]; // f_suff_S[i][j]: S[i..j] 构成 "AS" 形式(S 为后缀 * 串)的方案数
|
||||
|
||||
// 预处理数组
|
||||
bool is_star[505][505]; // is_star[i][j]: S[i..j] 是否能全变成 '*'
|
||||
|
||||
int main() {
|
||||
// 使用 OI/ACM 风格的快速 I/O
|
||||
ios_base::sync_with_stdio(false);
|
||||
cin.tie(NULL);
|
||||
|
||||
int n, k;
|
||||
cin >> n >> k;
|
||||
string s;
|
||||
cin >> s;
|
||||
|
||||
// 为了方便,我们将字符串和 DP 数组都使用 1-based 索引
|
||||
string S = " " + s;
|
||||
|
||||
// --- 预处理 is_star[i][j] ---
|
||||
// 一个子串 S[i..j] 能否全为 '*',取决于它是否包含 '(' 或 ')'
|
||||
// 我们可以用前缀和 O(1) 查询
|
||||
vector<int> prefix_L(n + 2, 0), prefix_R(n + 2, 0);
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
prefix_L[i] = prefix_L[i - 1] + (S[i] == '(');
|
||||
prefix_R[i] = prefix_R[i - 1] + (S[i] == ')');
|
||||
}
|
||||
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
for (int j = i; j <= n; ++j) {
|
||||
if ((prefix_L[j] - prefix_L[i - 1] == 0) && (prefix_R[j] - prefix_R[i - 1] == 0)) {
|
||||
is_star[i][j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 区间 DP 主循环 ---
|
||||
// len 是当前处理的子串长度
|
||||
for (int len = 2; len <= n; ++len) {
|
||||
for (int i = 1; i <= n - len + 1; ++i) {
|
||||
int j = i + len - 1;
|
||||
|
||||
// --- 1. 计算辅助状态 g[i][j] 和 f_suff_S[i][j] ---
|
||||
// 这两个状态的计算依赖于 f[...][...],而 f 的值在更小的 len 时已经算好
|
||||
|
||||
// g[i][j] (SA form): S[i..p] as S, S[p+1..j] as A
|
||||
for (int p = i; p <= min(j - 1, i + k - 1); ++p) {
|
||||
if (is_star[i][p]) {
|
||||
if (p + 1 <= j) {
|
||||
g[i][j] = (g[i][j] + f[p + 1][j]) % MOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// f_suff_S[i][j] (AS form): S[i..p] as A, S[p+1..j] as S
|
||||
for (int p = max(i, j - k); p < j; ++p) {
|
||||
if (is_star[p + 1][j]) {
|
||||
f_suff_S[i][j] = (f_suff_S[i][j] + f[i][p]) % MOD;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. 计算 A 类序列 h[i][j] ---
|
||||
if ((S[i] == '(' || S[i] == '?') && (S[j] == ')' || S[j] == '?')) {
|
||||
int i_inner = i + 1;
|
||||
int j_inner = j - 1;
|
||||
|
||||
// Base case: "()"
|
||||
if (len == 2) {
|
||||
h[i][j] = 1;
|
||||
} else if (i_inner <= j_inner) {
|
||||
int len_inner = j_inner - i_inner + 1;
|
||||
|
||||
// (S) form: inner part is all '*', length must be 1 to k
|
||||
if (len_inner <= k && is_star[i_inner][j_inner]) {
|
||||
h[i][j] = (h[i][j] + 1) % MOD;
|
||||
}
|
||||
|
||||
// (A) form: inner part is a valid SBS
|
||||
h[i][j] = (h[i][j] + f[i_inner][j_inner]) % MOD;
|
||||
|
||||
// (SA) form: inner part is S then A
|
||||
h[i][j] = (h[i][j] + g[i_inner][j_inner]) % MOD;
|
||||
|
||||
// (AS) form: inner part is A then S
|
||||
h[i][j] = (h[i][j] + f_suff_S[i_inner][j_inner]) % MOD;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 3. 计算任意合法序列 f[i][j] ---
|
||||
// 任意合法序列可以被唯一分解为 `A' R`
|
||||
// `A'` 是第一个 A 类子序列 S[i..p], `R` 是剩余部分 S[p+1..j]
|
||||
|
||||
// Case 1: 整个 S[i..j] 就是一个 A 类序列 (R 为空)
|
||||
f[i][j] = h[i][j];
|
||||
|
||||
// Case 2: S[i..p] 是第一个 A 类序列,S[p+1..j] 是非空剩余部分
|
||||
for (int p = i; p < j; ++p) {
|
||||
if (h[i][p] == 0) continue;
|
||||
|
||||
// R 可以是 'B' (AB form) 或 'SB' (ASB form)
|
||||
// 方案数总和为 f[p+1][j] + g[p+1][j]
|
||||
ll ways_A = h[i][p];
|
||||
ll ways_R = (f[p + 1][j] + g[p + 1][j]) % MOD;
|
||||
|
||||
f[i][j] = (f[i][j] + ways_A * ways_R) % MOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << f[1][n] << endl;
|
||||
}
|
Loading…
Reference in New Issue
Block a user