動態規劃-----編輯距離
編輯距離,計算從一個字串到另一個字串的最短編輯距離,其可以通過增、刪、替方式來實現。
例如:字串str1 = mitcmu,與字串str2=mtacnu,字串str1通過增、刪、替方式變成str2,最短編輯距離為3:
第一步:str1刪除字元i,變成str1=mtcmu;
第二步:str1在字元t後面增加字元a,變成str1=mtacmu;
第三步:str1中的字元m替換成n,變成str1=mtacnu = str2。
編輯距離,分為萊文斯坦距離與最長公共子串長度,兩者區別:萊文斯坦距離,從一個字串實現成為另一個字串的最少編輯距離,操作方式有增、刪、替方式;最長公共子串長度,則計算兩個字串之間的相同字串長度,操作方式有增、刪。
- 萊文斯坦距離
解題思路,回溯法:
(1).匹配str1[i]和str2[j]
a.匹配:
匹配str1[i+1]和str2[j+1]
b.不匹配:
1).可以刪除str1[i],然後遞迴考察str1[i+1]和str2[j]
2).可以刪除str2[j],然後遞迴考察str1[i]和str2[j+1]
3).可以在str1[i]前面新增一個跟str2[j]相同字元,讓遞迴考察str1[i]和str2[j+1]
4).可以在str2[j]前面新增一個跟str1[i]相同字元,讓遞迴考察str1[i+1]和str2[j]
5).可以將str1[i]替換成str2[j],或者將str2[j]替換成str1[i],然後遞迴考察str1[i+1]和str2[j+1]
決策圖用函式f(i,j,dis)表示樹,其中i表示第一個字串裡面的字元,j表示第二個字串的zif,dis表示距離:
由圖可以得出:編輯距離通過增、刪、替方式來獲取每個步驟的最短距離,由此可以獲取如下規律
從中我們可以得出狀態轉移方程:
(1)如果str1[i] != str2[j],那麼達到每一個步驟的最短距離
min_dis(i,j) = min(min_dis(i-1,j)+1,min_dis(i,j-1)+1,min_dis(i-1,j-1)+1)
(2)如果str1[i] == str2[j], 那麼達到每一個步驟的最短距離
min_dis(i,j) = min(min_dis(i-1,j),min_dis(i,j-1),min_dis(i-1,j-1))
由上,可得出動態規劃法:
(1)初始化表格
"" | m | t | a | c | n | u | |
"" | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
m | 1 | ||||||
i | 2 | ||||||
t | 3 | ||||||
c | 4 | ||||||
m | 5 | ||||||
u | 6 |
(2)第一行計算:根據回溯法,j計算m變成m,mt,mta,mtac,mtacn,mtacnu所需要的距離
"" | m | t | a | c | n | u | |
"" | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
m | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
i | 2 | ||||||
t | 3 | ||||||
c | 4 | ||||||
m | 5 | ||||||
u | 6 |
(3)第二行計算:根據回溯法,j計算mi變成m,mt,mta,mtac,mtacn,mtacnu所需要的距離
"" | m | t | a | c | n | u | |
"" | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
m | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
i | 2 | 1 | 1 | 2 | 3 | 4 | 5 |
t | 3 | ||||||
c | 4 | ||||||
m | 5 | ||||||
u | 6 |
(6)依此類推第六行計算:根據回溯法,j計算m變成m,mt,mta,mtac,mtacn,mtacnu所需要的距離
"" | m | t | a | c | n | u | |
"" | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
m | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
i | 2 | 1 | 1 | 2 | 3 | 4 | 5 |
t | 3 | 2 | 1 | 2 | 3 | 4 | 5 |
c | 4 | 3 | 2 | 2 | 2 | 3 | 4 |
m | 5 | 3 | 3 | 3 | 3 | 3 | 4 |
u | 6 | 4 | 4 | 4 | 4 | 4 | 3 |
因此程式碼實現如下
import numpy as np #編輯距離之萊溫斯坦距離,用於計算兩個字串之間的相似度 def LewinsteinDistance(strFirst,strSecond): #用於儲存每一步的最短距離的值 editDis = np.zeros(shape=(len(strFirst) + 1, len(strSecond) + 1)) for i in range(len(editDis)): for j in range(len(editDis[i])): #初始化i==0,即行為0時的值,為0,1,2...... if i == 0: editDis[i][j] = j # 初始化j==0,即列為0時的值,為0,1,2...... elif j == 0: editDis[i][j] = i else: #兩個字元不匹配 if strFirst[i-1] != strSecond[j-1]: editDis[i][j] = min(editDis[i-1][j-1]+1,editDis[i-1][j]+1, editDis[i][j-1]+1) else: # 兩個字元匹配 editDis[i][j] = min(editDis[i - 1][j - 1] , editDis[i - 1][j] , editDis[i][j - 1] ) for i in range(len(editDis)): for j in range(len(editDis[j])): print(editDis[i][j], end=" ") print() strSecond= "mtacnu" strFirst = "mitcmu" #strSecond= "flaw" #strFirst = "lawn" LewinsteinDistance(strFirst,strSecond)
結果輸出:
0.0 1.0 2.0 3.0 4.0 5.0 6.0 1.0 0.0 1.0 2.0 3.0 4.0 5.0 2.0 1.0 1.0 2.0 3.0 4.0 5.0 3.0 2.0 1.0 2.0 3.0 4.0 5.0 4.0 3.0 2.0 2.0 2.0 3.0 4.0 5.0 3.0 3.0 3.0 3.0 3.0 4.0 6.0 4.0 4.0 4.0 4.0 4.0 3.0
2.最長公共子串長度
狀態轉移方程:
(1) str1[i]==str2[j]:
max_Long(i,j)=max(max_Long(i-1,j-1)+1,max_Long(i-1,j),max_Long(i,j-1))
(2) str1[i] != str2[j]
max_long(i,j)=max(max_Long(i-1,j-1),max_Long(i-1,j),max_Long(i,j-1))
演算法實現:
import numpy as np #最長公共子串 def LongestCommonCubstring(strFirst,strSecond): editDis = np.zeros(shape=(len(strFirst) + 1, len(strSecond) + 1)) for i in range(1,len(editDis)): for j in range(1,len(editDis[i])): if strFirst[i - 1] != strSecond[j - 1]: editDis[i][j] = max(editDis[i - 1][j - 1] , editDis[i - 1][j] , editDis[i][j - 1] ) else: editDis[i][j] = max(editDis[i - 1][j - 1] + 1, editDis[i - 1][j], editDis[i][j - 1]) for i in range(len(editDis)): for j in range(len(editDis[j])): print(editDis[i][j], end=" ") print() strSecond= "mtacnu" strFirst = "mitcmu" LongestCommonCubstring(strFirst,strSecond)
輸出
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 1.0 1.0 1.0 0.0 1.0 2.0 2.0 2.0 2.0 2.0 0.0 1.0 2.0 2.0 3.0 3.0 3.0 0.0 1.0 2.0 2.0 3.0 3.0 3.0 0.0 1.0 2.0 2.0 3.0 3.0 4.0