編輯距離是指將乙個字串轉化為另外乙個字串,需要的最少的編輯操作次數,這些操作包括增加、刪除或者替換某個字元。可以看出,編輯距離越大,說明兩個字串的相似度越小;相反,編輯距離越小,說明這兩個字串的相似程度越大。兩個字串的編輯距離為0則說明這兩個字串相同。
編輯距離的計算方式比較多,比較著名的有萊文斯坦距離(levenshten distance)和最長公共子串長度(longest common substring length)。
比如:字串mitcmu和mtacnu的萊文斯坦距離是3,最長公共子串長度是4.
萊文斯坦距離就是找出兩個字串之間的差異度,即最小的編輯次數。
這個問題其實可以使用回溯來解決,看你能否想到,思路如下:
依次考察乙個字串中的每個字元,跟另乙個字串中的字元是否匹配,比如a[i]和b[j]如果匹配,則繼續考察a[i+1]和b[j+1].但如果a[i]和b[j]不匹配呢?如果不匹配,則有以下幾種方式來處理:
其實回溯計算的過程如果沒有重複子問題則直接就用回溯就可以了,但是如果有較多的重複子問題動態規劃則是更高效的方式。
dp思想解決問題的關鍵其實找到狀態轉移方式,而萊文斯坦距離的dp狀態轉移方式其實有三種,比如對於長度分別是n和m的字元陣列a和b來說:
如果:a[i]!=b[j],那麼:min_edist(i, j) 就等於:試著理解下:min(min_edist(i-1,j)+1, min_edist(i,j-1)+1, min_edist(i-1,j-1)+1)
如果:a[i]==b[j],那麼:min_edist(i, j) 就等於:
min(min_edist(i-1,j)+1, min_edist(i,j-1)+1,min_edist(i-1,j-1))
其中,min_edit(i,j)表示考察到a[i]和b[j]時經過的最小的編輯距離;min 表示求三數中的最小值。
無論是min_edist(i-1,j)還是min_edist(i,j-1),下乙個狀態如果是min_edist(i,j)必定是其中乙個且僅是乙個字串往後移動了乙個下標到達的min_edist(i,j)。這說明發生了編輯,編輯距離加一了。
當a[i]!=b[j]時, min_edist(i-1,j-1)需要加1容易理解因為a[i]和b[j]不等需要編輯。
當a[i]==b[j]時,min_edist(i-1,j-1)不需要加1,因為字元a[i]和b[j]相等不需要編輯。
這裡有點難理解:
min_edist(i,j-1)或者min_edist(i-1,j)轉移到min_edist(i,j)應該加1,因為min_edist(i,j-1)或者min_edist(i-1,j)表示的是編輯距離,而編輯距離表示的是達到次數x時兩個字串相等。既然相等就肯定同時往後移動下標,所以不能簡單理解為編輯距離,而應該思考min_edist(i-1,j)和min_edist(i,j)之間的狀態轉移關係。
mindist[i-1][j]或mindist[i][j-1]到mindist[i][j]的轉移,就是在字串中增加了乙個字元
比如hello和hllo,當i指向e,j指向l的時候,往hllo中加入字元e成為hello,那麼此時j-1就是e,
j是l,就相當於是mindist[i][j-1]到mindist[i][j]的轉移.
於是he和hel的編輯距離mindist[i][j]為mindist[i][j-1]+1
思路:如果 a[i] 與 b[j] 互相匹配,我們將最大公共子串長長度加一,並且繼續考察 a[i+1] 和 b[j+1]
如果 a[i] 與 b[j] 不匹配,最長公共子串長度不變,此時如下兩種處理方式:
狀態轉移公式:
如果:a[i]==b[j],那麼:max_lcs(i, j) 就等於: max(max_lcs(i-1,j-1)+1, max_lcs(i-1, j), max_lcs(i, j-1));如果:a[i]!=b[j],那麼:max_lcs(i, j) 就等於: max(max_lcs(i-1,j-1), max_lcs(i-1, j), max_lcs(i, j-1));
其中 max 表示求三數中的最大值。
package com.study.algorithm.dp;
/** * @auther: jeffsheng
* @date: 2019/10/21 16:02
* @description: 編輯距離的實現
* */
public
class
editdistance
if(j < m)
if(edist < mindist)
return;}
// 兩個字元匹配
if(a[i]
== b[j]
)else
}/**
* 動態規劃思想解決編輯距離問題
* 即由前乙個狀態決定後乙個狀態
* @param a 字元陣列a
* @param n a的長度n
* @param b 字元陣列b
* @param m b的長度m
* @return
*/public
intlwstdp
(char
a,int n,
char
b,int m)
else
if(j !=0)
else
}// 初始化第 0 列:a[0..i] 與 b[0..0] 的編輯距離
for(
int i =
0; i < n;
++i)
else
if(i !=0)
else
}// 按行填表
for(
int i =
1; i < n;
++i)
else}}
return mindist[n-1]
[m-1];
}private
intmin
(int x,
int y,
int z)
if(y < minv)
if(z < minv)
return minv;
}/**
* 動態規劃思想+最長公共子串長度法來計算編輯距離
* @param a
* @param n
* @param b
* @param m
* @return
*/public
intlcs
(char
a,int n,
char
b,int m)
else
if(j !=0)
else
}// 初始化第 0 列:a[0..i] 與 b[0..0] 的 maxlcs
for(
int i =
0; i < n;
++i)
else
if(i !=0)
else
}// 填表
for(
int i =
1; i < n;
++i)
else}}
return maxlcs[n-1]
[m-1];
}private
intmax
(int x,
int y,
int z)
if(y > maxv)
if(z > maxv)
return maxv;
}public
static
void
main
(string[
] args)
}
Edit Distance 編輯距離
編輯距離是一道經典的動態規劃 dynamic programing 問題。下面將詳細地介紹解法。我們先定義一下cache i j 表示,要將word1 0.i 1 轉化成word2 0.j 1 的最小步驟。比如,我們的word1 aeee world2 adef a def aeee 我們先來考慮一...
編輯距離 Edit distance
插入乙個字元,例如 fj fxj 刪除乙個字元,例如 fxj fj 替換乙個字元,例如 jxj fyj 用分治的思想解決比較簡單,將複雜的問題分解成相似的子問題 假設字串 a,共 m 位,從a 1 到a m 字串 b,共 n 位,從b 1 到b n d i j 表示字串a 1 a i 轉換為b 1 ...
編輯距離(Edit Distance)
edit distance 1000 ms 65535 kb 568 2526 設a 和b 是2 個字串。要用最少的字元操作將字串a 轉換為字串b。這裡所說的字元操作包括 1 刪除乙個字元 2 插入乙個字元 3 將乙個字元改為另乙個字元。將字串a變換為字串b 所用的最少字元運算元稱為字串a到b 的編...