- 问的是字符串A操作多少步能到字符串B,Actually,我们可以转换一下,将问题变成“同时操作字符串A、B,最少经过多少次操作,能将字符串A和B变为相同”
- 即将A、B向一个“中间状态”靠拢。这样的最小操作次数和把A转化成B的最小操作次数是相同的。
- 这样的好处是,不用考虑“增”的操作。 abc到abcde的编辑距离,可以是abc“增”两次,也可以是abcde“删”两次,所以我们只考虑删,不考虑增。
-
DP数组和下标的含义:
dp[i][j]
表示字符串A的前i
个,和字符串B的前j
个的最小编辑距离(字符串的下标从1开始) -
递推公式
- 当
a[i] == b[j]
,A的前i
个和B的前j
个的编辑距离,就等于A的前i-1
个和B的前j-1
个的编辑距离。(因为A的第i
个字符和B的第j
个字符相等,不需要操作,即不需要考虑) == 》dp[i][j] = dp[i - 1][j - 1]
。(即abcde
和bkje
的编辑距离等于abcd
和bkj
的编辑距离) - 当
a[i] != b[j]
,我们有3种可以选择的操作:(因为是求最小编辑距离,所以三者取min)- 字符串A删一个字符,
dp[i][j] = dp[i - 1][j] + 1
,此时A和B的编辑距离,就对A的前i-1
个字符和B的前i
个字符考虑,A[i]
被删了就不考虑了。加的这个1,就是删去A[i]
的一次操作次数。(即abcde
与wads
的编辑距离等于abcd与wads的编辑距离+1) - 字符串B删一个字符,同理,
dp[i][j] = dp[i][j - 1] + 1
。 - 替换字符,
dp[i][j] = dp[i - 1][j - 1] + 1
。替换字符后,A[i]
和B[j]
就一样了,所以就不用考虑了,直接考虑A的前i-1
个和B的前j-1
个,即dp[i - 1][j - 1]
,加上替换一次的次数1即可。(比如abcdf
和wasde
,f
换成e
之后,此时为abcde
,wasde
,两个字符串最后一个字符相等,转换为求abcd
和wasd
的编辑距离)
- 字符串A删一个字符,
- 当
-
DP数组初始化
dp[i][0] = i
,表示长度为i的字符串变成空串要删i次dp[0][i] = i
,同理
#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
int dp[N][N];
char a[N], b[N];
int main()
{
cin >> a + 1; // 下标从1开始
cin >> b + 1;
int alen = strlen(a + 1);
int blen = strlen(b + 1); // 最后答案就是dp[alen][blen]
for (int i = 0; i <= alen; i ++) dp[i][0] = i; // 初始化
for (int i = 0; i <= blen; i ++) dp[0][i] = i;
for (int i = 1; i <= alen; i ++)
for (int j = 1; j <= blen; j ++)
{
if (a[i] == b[j]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
}
cout << dp[alen][blen] << endl;
return 0;
}