CodeForces 1701E Text Editor
思路
手玩几组数据可知:
-
若不回到开头删字符,则操作次数为 \(n - pl\),其中 \(pl\) 表示 \(s\) 和 \(t\) 的 \(\mathrm{LCP}\)。
-
若需要回到开头删字符,则最优解一定是光标先从右往左移动一段,若有不同的字符则按一次
backspace
;然后按home
回到开头,光标从左往右移动一段,若有不同的字符则需要先按right
再按backspace
。中间有一段是跟 \(t\) 中相同长度的一段匹配的,因此不用动。
可以把 \(s\) 分成前、中、后三部分,然后 dp。设 \(f_{i,j,0/1/2}\) 表示当前处理到 \(s\) 的第 \(i\) 个字符,跟 \(t\) 中前 \(j\) 个字符匹配,当前光标在前/中/后部分的最少移动次数。
转移:
- 任意时刻,若光标在前/后部分,都可以删除当前字符。在前部分删除一个字符的代价是 \(2\)(因为要先按
right
再按backspace
),在后部分删除一个字符的代价是 \(1\)。因此:
\[f_{i,j,0} \gets \min(f_{i,j,0},f_{i-1,j,0}+2)
\]
\[f_{i,j,2} \gets \min(f_{i,j,2},f_{i-1,j,2}+1)
\]
注意这里没有 \(f_{i,j,1}\) 的转移,是因为中部分的字符不能修改。
- 当 \(s_i = t_j\),若光标在前/后部分,则直接按
right
/left
,匹配长度增加 \(1\)。若光标在中部分,则直接沿用之前的值。因此:
\[f_{i,j,0} \gets \min(f_{i,j,0},f_{i-1,j-1,0}+1)
\]
\[f_{i,j,1} \gets \min(f_{i,j,1},f_{i-1,j-1,1})
\]
\[f_{i,j,2} \gets \min(f_{i,j,2},f_{i-1,j-1,2}+1)
\]
- 任意时刻,光标都可以从前部分转入中部分/后部分,或从中部分转入后部分。因此:
\[f_{i,j,1} \gets \min(f_{i,j,1},f_{i,j,0})
\]
\[f_{i,j,2} \gets \min(f_{i,j,2},f_{i,j,1},f_{i,j,0})
\]
注意最后还要加上按 home
的操作,即操作次数为 \(f_{n,m,2}+1\)。
由于此题卡空间,所以要用滚动数组优化。
代码
code
/*
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int maxn = 5050;
const int inf = 0x3f3f3f3f;
int n, m, f[2][maxn][3];
char s[maxn], t[maxn];
void solve() {
scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
int j = 0;
for (int i = 1; i <= n; ++i) {
if (j < m && t[j + 1] == s[i]) {
++j;
}
}
if (j < m) {
puts("-1");
return;
}
int pl = m;
for (int i = 1; i <= m; ++i) {
if (s[i] != t[i]) {
pl = i - 1;
break;
}
}
int cur = 0;
for (int i = 0; i <= m; ++i) {
f[0][i][0] = f[0][i][1] = f[0][i][2] = inf;
}
f[0][0][0] = f[0][0][1] = f[0][0][2] = 0;
for (int i = 1; i <= n; ++i) {
cur ^= 1;
for (int j = 0; j <= m; ++j) {
f[cur][j][0] = f[cur][j][1] = f[cur][j][2] = inf;
}
for (int j = 0; j <= m; ++j) {
f[cur][j][0] = min(f[cur][j][0], f[cur ^ 1][j][0] + 2);
f[cur][j][2] = min(f[cur][j][2], f[cur ^ 1][j][2] + 1);
if (j && s[i] == t[j]) {
f[cur][j][0] = min(f[cur][j][0], f[cur ^ 1][j - 1][0] + 1);
f[cur][j][1] = min(f[cur][j][1], f[cur ^ 1][j - 1][1]);
f[cur][j][2] = min(f[cur][j][2], f[cur ^ 1][j - 1][2] + 1);
}
}
for (int j = 0; j <= m; ++j) {
f[cur][j][1] = min(f[cur][j][1], f[cur][j][0]);
f[cur][j][2] = min(f[cur][j][2], f[cur][j][1]);
}
}
printf("%d\n", min(n - pl, f[cur][m][2] + 1));
}
int main() {
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}