题意
给定两个字符串(A)和(B),现在要将(A)经过若干操作变为(B),可进行的操作有:
- 删除–将字符串A中的某个字符删除。
- 插入–在字符串A的某个位置插入某个字符。
- 替换–将字符串A中的某个字符替换为另一个字符。
现在请你求出,将(A)变为(B)至少需要进行多少次操作。
数据范围
(1 leq |A|, |B| leq 1000)
思路
受最长公共子序列的启发,我们设(f(i, j))表示字符串(A[1 sim i])变为(B[1 sim j])需要进行多少次操作(下标从(1)开始)。
考虑(A)的第(i)位和(B)的第(j)位,如果(A[i] = B[j]),那么(f(i, j) = f(i - 1, j - 1))
如果(A[i] eq B[j]),考虑三种操作:
- 如果进行删除操作,就是删除(A)的第(i)位,那么(f(i, j) = f(i - 1, j) + 1)
- 如果进行插入操作,相当于对(B)进行删除操作,就是删除(B)的第(j)位,因此(f(i, j) = f(i, j - 1) + 1)
- 如果进行替换操作,就是将(A)的第(i)位替换为(B)的第(j)位,因此(f(i, j) = f(i - 1, j - 1) + 1)
将这三种情况取最小即可。
下面考虑边界,考虑边界一般从遍历过程中用到的,但是之前没有初始化的值。这里就是(f(i, 0) = i, 1 leq i leq n)以及(f(0, i) = i, 1 leq i leq m)
代码
递推
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];
int main()
{
scanf("%d%s%d%s", &n, a + 1, &m, b + 1);
for(int i = 1; i <= n; i ++) f[i][0] = i;
for(int i = 1; i <= m; i ++) f[0][i] = i;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
if(a[i] == b[j]) f[i][j] = f[i - 1][j - 1];
else f[i][j] = min(f[i - 1][j], min(f[i][j - 1], f[i - 1][j - 1])) + 1;
}
}
printf("%d
", f[n][m]);
return 0;
}
记忆化搜索
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];
int dfs(int i, int j)
{
if(i == 0) return j;
if(j == 0) return i;
if(f[i][j] > 0) return f[i][j];
if(a[i] == b[j]) f[i][j] = dfs(i - 1, j - 1);
else f[i][j] = min(dfs(i - 1, j), min(dfs(i, j - 1), dfs(i - 1, j - 1))) + 1;
return f[i][j];
}
int main()
{
scanf("%d%s%d%s", &n, a + 1, &m, b + 1);
printf("%d
", dfs(n, m));
return 0;
}