最短编辑距离

题意

给定两个字符串(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;
}
原文地址:https://www.cnblogs.com/miraclepbc/p/14432605.html