W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
編輯距離,也被稱為 Levenshtein 距離,指兩個字符串之間互相轉(zhuǎn)換的最小修改次數(shù),通常用于在信息檢索和自然語言處理中度量兩個序列的相似度。
Question
輸入兩個字符串
你可以在一個字符串中進(jìn)行三種編輯操作:插入一個字符、刪除一個字符、替換字符為任意一個字符。
如圖 14-27 所示,將 kitten
轉(zhuǎn)換為 sitting
需要編輯 3 步,包括 2 次替換操作與 1 次添加操作;將 hello
轉(zhuǎn)換為 algo
需要 3 步,包括 2 次替換操作和 1 次刪除操作。
圖 14-27 編輯距離的示例數(shù)據(jù)
編輯距離問題可以很自然地用決策樹模型來解釋。字符串對應(yīng)樹節(jié)點(diǎn),一輪決策(一次編輯操作)對應(yīng)樹的一條邊。
如圖 14-28 所示,在不限制操作的情況下,每個節(jié)點(diǎn)都可以派生出許多條邊,每條邊對應(yīng)一種操作,這意味著從 hello
轉(zhuǎn)換到 algo
有許多種可能的路徑。
從決策樹的角度看,本題的目標(biāo)是求解節(jié)點(diǎn) hello
和節(jié)點(diǎn) algo
之間的最短路徑。
圖 14-28 基于決策樹模型表示編輯距離問題
第一步:思考每輪的決策,定義狀態(tài),從而得到
每一輪的決策是對字符串
我們希望在編輯操作的過程中,問題的規(guī)模逐漸縮小,這樣才能構(gòu)建子問題。設(shè)字符串
也就是說,我們在字符串
狀態(tài)
至此,得到一個尺寸為
第二步:找出最優(yōu)子結(jié)構(gòu),進(jìn)而推導(dǎo)出狀態(tài)轉(zhuǎn)移方程
考慮子問題
圖 14-29 編輯距離的狀態(tài)轉(zhuǎn)移
根據(jù)以上分析,可得最優(yōu)子結(jié)構(gòu):
請注意,當(dāng)
第三步:確定邊界條件和狀態(tài)轉(zhuǎn)移順序
當(dāng)兩字符串都為空時,編輯步數(shù)為
觀察狀態(tài)轉(zhuǎn)移方程,解
edit_distance.cpp
/* 編輯距離:動態(tài)規(guī)劃 */
int editDistanceDP(string s, string t) {
int n = s.length(), m = t.length();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// 狀態(tài)轉(zhuǎn)移:首行首列
for (int i = 1; i <= n; i++) {
dp[i][0] = i;
}
for (int j = 1; j <= m; j++) {
dp[0][j] = j;
}
// 狀態(tài)轉(zhuǎn)移:其余行列
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i - 1] == t[j - 1]) {
// 若兩字符相等,則直接跳過此兩字符
dp[i][j] = dp[i - 1][j - 1];
} else {
// 最少編輯步數(shù) = 插入、刪除、替換這三種操作的最少編輯步數(shù) + 1
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
}
}
}
return dp[n][m];
}
如圖 14-30 所示,編輯距離問題的狀態(tài)轉(zhuǎn)移過程與背包問題非常類似,都可以看作是填寫一個二維網(wǎng)格的過程。
圖 14-30 編輯距離的動態(tài)規(guī)劃過程
由于
為此,我們可以使用一個變量 leftup
來暫存左上方的解
edit_distance.cpp
/* 編輯距離:空間優(yōu)化后的動態(tài)規(guī)劃 */
int editDistanceDPComp(string s, string t) {
int n = s.length(), m = t.length();
vector<int> dp(m + 1, 0);
// 狀態(tài)轉(zhuǎn)移:首行
for (int j = 1; j <= m; j++) {
dp[j] = j;
}
// 狀態(tài)轉(zhuǎn)移:其余行
for (int i = 1; i <= n; i++) {
// 狀態(tài)轉(zhuǎn)移:首列
int leftup = dp[0]; // 暫存 dp[i-1, j-1]
dp[0] = i;
// 狀態(tài)轉(zhuǎn)移:其余列
for (int j = 1; j <= m; j++) {
int temp = dp[j];
if (s[i - 1] == t[j - 1]) {
// 若兩字符相等,則直接跳過此兩字符
dp[j] = leftup;
} else {
// 最少編輯步數(shù) = 插入、刪除、替換這三種操作的最少編輯步數(shù) + 1
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1;
}
leftup = temp; // 更新為下一輪的 dp[i-1, j-1]
}
}
return dp[m];
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: