[解题记录] NOI Online 2022 入门组 字符串
NOI Online 2022 入门组 字符串
题意简述
给定两个字符串 \(S\) 和 \(T\) 和一个初始为空的字符串 \(R\),其中 \(S\) 长度为 \(n\),且只由 0, 1, - 三种字符构成(注:这里的第三种字符是减号), 然后进行 \(n\) 次操作, 每次会取出 \(S\) 的第一个字符(记为 \(c\)),并将其从 \(S\) 中删去。如果 \(c = \texttt{-}\),则要删去 \(R\) 的开头字符或结尾字符(数据保证删去后 \(R\) 不为空)。将 \(c\) 加入到 \(R\) 的末尾。问将 \(R\) 变成 \(T\) 的方案数。
解题思路
首先不难想到一个 \(\mathcal O(2^{n/2})\) 的爆搜,就是枚举遇到减号时的决策,这样可以拿 \(30pts\)
看到题目的数据范围,以及题目要我们求方案数,就联想到 DP, 我们定义状态 \(f[i][j][x][y]\) 表示 \(S\) 操作到第 \(i\) 个, 成功匹配了 \(T\) 的前 \(j\) 个数, \(R\) 前面要删除 \(x\) 个数,后面要删除 \(y\) 个数的方案,那么状态转移方程就是 :
- 若 \(S_i=-\), \(f[i + 1][j][x - 1][y] += f[i][j][x][y]\)
- 若 \(S_i \ne -\), \(f[i + 1][j][x][l+1] += f[i][j][x][y]\)
- 若 \(S_i \ne -\) 且 \(S_i = T_{j - 1}\) 且 \(l = 0\), \(f[i+1][j +1][x][y] += f[i][j][x][y]\)
复杂度是 \(\mathcal O(n ^ 3m)\) 的,不能通过所有数据,但是我们发现其实最后一维完全没有必要记录,因为每次操作完后,\(R\) 的字符数是确定的,所以只要知道前面有几个要删除,成功匹配了几个,状态就是唯一确定的,去掉一维后复杂度就变成 \(\mathcal O (n ^ 2m)\) 的,但是实现一下就会发现 DP 要处理的细节非常多,我们所以可以写 记忆化搜索 !
Code
DP
#include <bits/stdc++.h>
using namespace std;
const int N = 410, mod = 1e9 + 7;
int f[N][N][N], n, m, T;
char s[N], t[N];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
memset(f, 0, sizeof(f));
int sum = 0;
f[0][0][0] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i] == '-') sum ++;
for (int j = 0; j <= m; ++j) {
for (int k = 0; k <= n; ++k) {//结尾
int l = i - 2 * sum - k - j;
if (l < 0) continue;
if (s[i] == '-') f[i][j][k] = (f[i - 1][j][k + 1] + f[i - 1][j][k]) % mod;//删左和删右
else {
if (k != 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1]) % mod;//放后面
else if (s[i] == t[j]) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k]) % mod;
if (!k && !j) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % mod;
}
}
}
}
printf("%d\n", f[n][m][0]);
}
return 0;
}
dfs
#include <bits/stdc++.h>
using namespace std;
const int N = 410, mod = 1e9 + 7;
int f[N][N][N], n, m, T;
char s[N], t[N];
int dfs(int i, int j, int x, int y) {//表示操作到s[i],匹配成功j个,前面要删x个,后面删除y个走下去成功的方案
if (i > n) return j == m && !x && !y;
if (~f[i][j][x]) return f[i][j][x];
int val = 0;
if (s[i] == '-') {
if (x) val = (val + dfs(i + 1, j, x - 1, y)) % mod;//删前面的
if (y) val = (val + dfs(i + 1, j, x, y - 1)) % mod;//删后面的
}
else {
val = (val + dfs(i + 1, j, x, y + 1)) % mod;//直接放到后面
if (!j && !y) val = (val + dfs(i + 1, j, x + 1, y)) % mod;//如果中间和后面都是空的
if (s[i] == t[j + 1] && !y) val = (val + dfs(i + 1, j + 1, x, y)) % mod;//如果匹配成功
}
return f[i][j][x] = val;
}
int main() {
scanf("%d", &T);
while (T--) {
memset(f, -1, sizeof(f));
scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
printf("%d\n", dfs(1, 0, 0, 0));
}
return 0;
}

浙公网安备 33010602011771号