CodeForces 1701E Text Editor

洛谷传送门

CF 传送门

思路

手玩几组数据可知:

  • 若不回到开头删字符,则操作次数为 \(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;
}
posted @ 2022-07-16 11:16  zltzlt  阅读(69)  评论(0编辑  收藏  举报