1. 程式人生 > 其它 >動態規劃-----編輯距離

動態規劃-----編輯距離

  編輯距離,計算從一個字串到另一個字串的最短編輯距離,其可以通過增、刪、替方式來實現。

  例如:字串str1 = mitcmu,與字串str2=mtacnu,字串str1通過增、刪、替方式變成str2,最短編輯距離為3:

  第一步:str1刪除字元i,變成str1=mtcmu;

  第二步:str1在字元t後面增加字元a,變成str1=mtacmu;

  第三步:str1中的字元m替換成n,變成str1=mtacnu = str2。

  編輯距離,分為萊文斯坦距離與最長公共子串長度,兩者區別:萊文斯坦距離,從一個字串實現成為另一個字串的最少編輯距離,操作方式有增、刪、替方式;最長公共子串長度,則計算兩個字串之間的相同字串長度,操作方式有增、刪。

  1. 萊文斯坦距離

  解題思路,回溯法

  (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