「CF1366G」 Construct the String「DP」
Posted hongzy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「CF1366G」 Construct the String「DP」相关的知识,希望对你有一定的参考价值。
CF1366G Construct the String
题解
我们把题目中的函数 (f(s)) 叫做「转换」。
用一个很朴素的dp:(f_{i,j}) 表示 (s) 前 (i) 位最少删除几个位置使得转换后等于 (t) 前 (j) 个位置。
边界就是 (f_{0,0}=0),转移分几种情况:
- 直接删去 (i + 1) :(f_{i,j} ightarrow f_{i+1},j)
- 若 (s_{i+1}=t_{j+1}),往后匹配一位:(f_{i,j} ightarrow f_{i+1,j+1})
- 若 (s_{i+1}) 为点:(f_{i,j} ightarrow f_{i+1,j-1})
这样够了吗?不够。还漏了一种情况:加的字符无法与 (t) 匹配,但最后都用点删去了,还是合法的转移。
预处理一个 (a_i) 表示串 (s) 从 (i) 往后长度最短、满足转换后为空串的串长度。再加个转移 (f_{i,j} ightarrow f_{i + a_{i+1},j})。
考虑正确性,转移显然都合法,现证明最优解能取到。最优方案中最终形成 (t) 的“骨架”拎起来,那骨架的每段间隙中没有删除的位置形成的一定是空串。
给一个串 (S),选一个子序列转换后是空串,最优选取方案一定是:把字母做 (
,点做)
,遇到(
就选,遇到)
时候有左括号配对就选,最后再把多余的(
退回去。你会发现选出的是一些子段,每一段都是合法的括号序列。
而我们的 (a_i) 就表示 (i) 开始的最短的括号序列且不能分割成两半。而拼接的情况如(())()
可以通过(())
和()
两次转移实现。
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
template<typename T>
void chkmin(T &x, const T &y) { if(x > y) x = y; }
const int N = 1e4 + 5, INF = 2 * N;
char s[N], t[N];
short dp[N][N], a[N];
int n, m;
int main() {
scanf("%s%s", s + 1, t + 1);
n = strlen(s + 1); m = strlen(t + 1);
for(int i = 1; i <= n; i ++) {
int y = 0;
for(int j = i; j <= n; j ++) {
if(s[j] == ‘.‘) {
if(!y) break ;
else {
y --; a[i] ++;
if(!y) break ;
}
} else {
a[i] ++; y ++;
}
}
if(y) a[i] = 0;
}
for(int i = 0; i <= n; i ++) fill(dp[i], dp[i] + m + 1, INF);
dp[0][0] = 0;
for(int i = 0; i <= n; i ++) {
for(int j = 0; j <= m; j ++) if(dp[i][j] < INF) {
if(i < n) chkmin(dp[i + 1][j], short(dp[i][j] + 1));
if(i < n && j < m && s[i + 1] == t[j + 1]) chkmin(dp[i + 1][j + 1], dp[i][j]);
if(j && i < n && s[i + 1] == ‘.‘) chkmin(dp[i + 1][j - 1], dp[i][j]);
if(a[i + 1]) chkmin(dp[i + a[i + 1]][j], dp[i][j]);
}
}
printf("%d
", (int) dp[n][m]);
return 0;
}
以上是关于「CF1366G」 Construct the String「DP」的主要内容,如果未能解决你的问题,请参考以下文章
CF914GSum the Fibonacci 快速??变换模板
CF914G Sum the Fibonacci(FWT,FST)
CF1486 C2. Guessing the Greatest (hard version)