题意
给出\(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;
}
}
}