【DP】【GDOI2017 day2】小学生语文题

题意

给出\(S\)串和\(T\)串。
其中\(T\)串可以通过每次拿出一个字符往前放的操作,最终变成\(S\)串。
求最少操作次数和操作方案。
共有\(T\)组数据。
\(1\leq len\leq 2000, 2\leq T\leq 10\)

思路

设\(f_{i,j}\)为\(S\)串的\(i\sim n\)与\(T\)串的\(j\sim n\)匹配的最小操作次数,其中\(T\)串有\(i-j\)个字符被拿了出来。
观察到\(f_{i,j}\)会从\(f_{i+1,j},f_{i,j+1},f_{i+1,j+1}\)三种情况推过来:
1、\(f_{i+1,j}\):即\(T\)串中\(j\sim n\)中拿出来的字符可以匹配\(S_i\),这时我们用后缀和判断一下是否可以转移。
2、\(f_{i,j+1}+1\):即把\(T_j\)拿出来。
3、\(f_{i+1,j+1}\):即\(S_i=T_j\),这时可以直接匹配,不用操作。
对于每次转移,我们都记录\(f_{i,j}\)是从哪里转移的。如果\((i+1,j+1)\to (i,j)\),那么此时不用操作,否则就往后枚举第一个没有匹配的\(j\)与\(i\)匹配。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

int T, n;
int sufs[2001][27], suft[2001][27], f[2001][2001], pre[2001][2001][2], bz[2001][2];
char s[2001], t[2001];

int main() {
	freopen("chinese.in", "r", stdin);
	freopen("chinese.out", "w", stdout);
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s + 1);
		scanf("%s", t + 1);
		n = strlen(s + 1);
		memset(sufs, 0, sizeof(sufs));
		memset(suft, 0, sizeof(suft));
		memset(pre, 0, sizeof(pre));
		memset(bz, 0, sizeof(bz));
		for (int i = n; i >= 1; i--) {
			sufs[i][s[i] - 96]++;
			suft[i][t[i] - 96]++;
			for (int j = 1; j <= 26; j++)
				sufs[i][j] += sufs[i + 1][j],
				suft[i][j] += suft[i + 1][j];
		}
		memset(f, 127 / 3, sizeof(f));
		for (int i = 0; i <= n; i++)
			f[n + 1][n - i + 1] = i;
		for (int i = n; i >= 1; i--)
			for (int j = i; j >= 1; j--) {
				if (suft[j][s[i] - 96] > sufs[i + 1][s[i] - 96] && f[i][j] > f[i + 1][j])
					f[i][j] = f[i + 1][j], pre[i][j][0] = i + 1, pre[i][j][1] = j;
				if (s[i] == t[j] && f[i][j] > f[i + 1][j + 1])
					f[i][j] = f[i + 1][j + 1], pre[i][j][0] = i + 1, pre[i][j][1] = j + 1;
				if (f[i][j] > f[i][j + 1] + 1)
					f[i][j] = f[i][j + 1] + 1, pre[i][j][0] = i, pre[i][j][1] = j + 1;
			}
		printf("%d\n", f[1][1]);
		for (int i = 1, j = 1; i <= n;) {
			if (pre[i][j][0] == i + 1 && pre[i][j][1] == j + 1)//不断往后跳
				bz[i][0] = bz[j][1] = 1;
			int x = i, y = j;
			i = pre[x][y][0];
			j = pre[x][y][1];
		}
		for (int i = 1; i <= n; i++)
			if (!bz[i][0])
				for (int j = i + 1; j <= n; j++)
					if (!bz[j][1] && s[i] == t[j]) {
						printf("%d %d\n", j, i);
						bz[j][1] = 1;
						for (int k = j; k > i; k--)
							std::swap(bz[k][1], bz[k - 1][1]), std::swap(t[k], t[k - 1]);//匹配之后要更新标记及T串
						break;
					}
	}
}
上一篇:Spring高级注解-Day2


下一篇:HDU多校day2-1010I love permutation