动态规划算法之编辑距离
一、什么是编辑距离
编辑距离(Edit distance,简称EDS),就是求字符串s1到字符串s2的最少修改次数。每次修改的方式如下:
1.增加一个字符。如:
a
b
c
abc
abc ->
a
b
c
d
abcd
abcd
2.删除一个字符。如:
a
b
c
abc
abc ->
a
b
ab
ab
3.修改一个字符。如:
a
b
c
abc
abc ->
a
b
d
abd
abd
二、编辑距离怎么求
1.深搜枚举
深搜枚举的时间复杂度很高。假设字符串只有小写字母,最长的字符串长度为 n n n,则时间复杂度为 O ( 2 6 n ) O(26^n) O(26n),远远超出题目范围。
2.动态规划(重点)
举个例子
以 h a s has has与 h a v e have have为例,要求 h a s has has与 h a v e have have的编辑距离,就可以把它转化成 h a s has has与 h a v hav hav的编辑距离。那么我们就可以创建一个 d p dp dp数组,其中, d p [ i ] [ j ] dp[i][j] dp[i][j]表示从a串的第一个字符到第i个字符与b串第一个字符到第j个字符的编辑距离。如下图所示:
^ | h | a | v | e | |
---|---|---|---|---|---|
^ | 0 | ||||
h | |||||
a | |||||
s |
第一步, d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]将更新为0。毕竟 空串 空串 空串与 空串 空串 空串与的编辑距离也是0嘛。
^ | h | a | v | e | |
---|---|---|---|---|---|
^ | 0 | 1 | 2 | 3 | 4 |
h | 1 | ||||
a | 2 | ||||
s | 3 |
第二步,更新 d p [ 0 ] [ j ] dp[0][j] dp[0][j]与 d p [ i ] [ 0 ] dp[i][0] dp[i][0],其中, d p [ i ] [ 0 ] = i dp[i][0] = i dp[i][0]=i, d p [ 0 ] [ j ] = j dp[0][j] = j dp[0][j]=j。
^ | h | a | v | e | |
---|---|---|---|---|---|
^ | 0 | 1 | 2 | 3 | 4 |
h | 1 | 0 | |||
a | 2 | ||||
s | 3 |
第三步,就要正式模拟了。先看 h h h和 h h h,由于他们相等,那就是 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i−1][j−1]了,因为这两个字符相等,不用做任何改变。
^ | h | a | v | e | |
---|---|---|---|---|---|
^ | 0 | 1 | 2 | 3 | 4 |
h | 1 | 0 | 1 | ||
a | 2 | ||||
s | 3 |
第四步,再看 h h h和 a a a,他们不相同,就要从 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i−1][j]、 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i−1][j−1]、 d p [ i ] [ j − 1 ] dp[i][j - 1] dp[i][j−1]里面选一个最小值,加上1。加上一的原因是要对某个字符进行改变。
^ | h | a | v | e | |
---|---|---|---|---|---|
^ | 0 | 1 | 2 | 3 | 4 |
h | 1 | 0 | 1 | 2 | 3 |
a | 2 | 1 | 0 | 1 | 2 |
s | 3 | 2 | 1 | 2 | 2 |
第五步,以此类推。最后, d p [ a l e n ] [ b l e n ] dp[alen][blen] dp[alen][blen]就是答案。
关键来了!
得出的转移方程:
1.
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
,
a
[
i
]
=
b
[
j
]
dp[i][j]=dp[i - 1][j - 1],a[i] = b[j]
dp[i][j]=dp[i−1][j−1],a[i]=b[j]
2.
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
+
1
,
a
[
i
]
!
=
b
[
j
]
dp[i][j]=min(dp[i - 1][j - 1],dp[i - 1][j],dp[i][j - 1])+1,a[i]!=b[j]
dp[i][j]=min(dp[i−1][j−1],dp[i−1][j],dp[i][j−1])+1,a[i]!=b[j]
边界值为:
d
p
[
i
]
[
0
]
=
i
,
d
p
[
0
]
[
j
]
=
j
dp[i][0]=i,dp[0][j]=j
dp[i][0]=i,dp[0][j]=j
上代码!
#include<iostream>
#include<cstring>
using namespace std;
int dp[105][105];
int min(int a, int b, int c){
if(a < b){
if(a < c)return a;
return c;
}
if(b < c)return b;
return c;
}
int main(){
string s1, s2;
cin >> s1 >> s2;
int lena, lenb;
lena = s1.length();//可用s1.size()代替,注意导入cstring库
lenb = s2.length();
if(s1[0] == s2[0]){
dp[0][0] = 0;
} else {
dp[0][0] = 1;
}
for(int i = 0; i < lenb; i++){
dp[0][i] = i;
}
for(int i = 0; i < lena; i++){
dp[i][0] = i;
}
for(int i = 1; i < lena; i++){
for(int j = 1; j < lena; j++){
if(s1[i] == s2[j]){
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1], dp[i][j - 1]) + 1;
}
}
}
cout << dp[lena][lenb];
return 0;
}