LCS详解
LCS是什么
LCS是Longest Common Subsequence的縮寫,即最長公共子序列。一個序列,如果是兩個或者多個序列的子序列,并且是所有子序列中最長的,則為最長公共子序列。(有序但不連續也為子序列)
- 序列 13456 和 345674 的最長公共子序列為 3456
- 序列 ABDBC 和 BCDBA 的最長公共子序列為 BDB
LCS可以用來做什么
- 生物學上用來進行基因序列比對,以推測序列的結構、功能和演化過程
- 用來描述兩段文字的”相似性“,可以用來辨別是不是抄襲
怎么計算LCS
-
暴力窮舉法
就是把兩個序列所有的子序列都列出來,然后一一進行比較。
假定字符串 A 和 B 的長度分別為 n 和 m,那么 A 共有 2n?12^n-12n?1 個子序列,B 共有 2m?12^m-12m?1 個子序列,然后將任意兩個進行一一比較,最后得出 A 和 B 的最長公共子序列。這種算法的時間復雜度是 O(2n+m)O(2^{n+m})O(2n+m) ,復雜度太高,當然不推薦使用。
-
動態規劃法
記:
字符串 A ,長度為 n ,從 1 開始;字符串 A ,長度為 n ,從 1 開始。
Ai=<A1,A2,...Ai>A_i=<A_1,A_2,...Ai>Ai?=<A1?,A2?,...Ai> 即 A 序列的前 i 個字符 (1≤i≤n)(1\leq i \leq n)(1≤i≤n) (AiA_iAi? 計做”字符串 A 的 i 前綴)
Bj=<B1,B2,...Bj>B_j=<B_1,B_2,...Bj>Bj?=<B1?,B2?,...Bj> 即 B 序列的前 j 個字符 (1≤j≤m)(1\leq j \leq m)(1≤j≤m) (BjB_jBj? 計做”字符串 B 的 j 前綴)
如果 An=BmA_n=B_mAn?=Bm? (最后一個字符相同),那么 A 和 B 的最長公共子序列 C 的最后一位 Ck=An=BmC_k=A_n=B_mCk?=An?=Bm? ,那么 LCS(A,B)=LCS(An?1,Bm?1)+AnLCS(A,B)=LCS(A_n-1,B_m-1)+A_nLCS(A,B)=LCS(An??1,Bm??1)+An?
如果 An?=BmA_n\not=B_mAn???=Bm? ,那么他們的最長公共子序列 C 要么是 LCS(An?1,Bm)LCS(A_{n-1},B_m)LCS(An?1?,Bm?) ,要么是 LCS(An,Bm?1)LCS(A_n,B_{m-1})LCS(An?,Bm?1?) ,所以 LCS(A,B)=max{LCS(An?1,Bm),LCS(An,Bm?1)}LCS(A,B)=max\{LCS(A_{n-1},B_m),LCS(A_n,B_{m-1})\}LCS(A,B)=max{LCS(An?1?,Bm?),LCS(An?,Bm?1?)}
1234567 A B D C A B A B A B C B D A B A3=B3=′C′A_3=B_3= 'C'A3?=B3?=′C′ 那么 LCS(BDC,ABC)=LCS(BD,AB)+′C′LCS(BDC,ABC)=LCS(BD,AB)+'C'LCS(BDC,ABC)=LCS(BD,AB)+′C′
A5=B4=′B′A_5=B_4='B'A5?=B4?=′B′ 那么 LCS(BDCAB,ABCB)=LCS(BDCA,ABC)+′B′LCS(BDCAB,ABCB)=LCS(BDCA,ABC)+'B'LCS(BDCAB,ABCB)=LCS(BDCA,ABC)+′B′
A2?=B2A_2\not=B_2A2???=B2? 那么 LCS(BD,AB)=max{LCS(B,AB),LCS(BD,A)}LCS(BD,AB)=max\{LCS(B,AB),LCS(BD,A)\}LCS(BD,AB)=max{LCS(B,AB),LCS(BD,A)}
A4?=B5A_4\not=B_5A4???=B5? 那么 LCS(BDCA,ABCBD)=max{LCS(BDC,ABCBD),LCS(BDCA,ABCB)}LCS(BDCA,ABCBD)=max\{LCS(BDC,ABCBD),LCS(BDCA,ABCB)\}LCS(BDCA,ABCBD)=max{LCS(BDC,ABCBD),LCS(BDCA,ABCB)}
由以上可以得出
LCS(An,Bm)={LCS(An?1,Bm?1+An)An=Bmmax{LCS(An?1,Bm),LCS(An,Bm?1)}An?=BmLCS(A_n,B_m)=\begin{cases}LCS(A_{n-1},B_{m-1}+A_n) \quad \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \quad A_n=B_m\\ max\{LCS(A_{n-1},B_m),LCS(A_n,B_{m-1})\} \quad A_n\not=B_m\end{cases} LCS(An?,Bm?)={LCS(An?1?,Bm?1?+An?)????????????????????????An?=Bm?max{LCS(An?1?,Bm?),LCS(An?,Bm?1?)}An???=Bm??
使用動態規劃法求解
首先上一幅圖
記一個二維數組 c[m,n]c[m,n]c[m,n],c[i,j]c[i,j]c[i,j] 的值為 xix_ixi? 和 yjy_jyj? 的最長公共子序列的長度,然后不難得出當 i=0i=0i=0 或 j=0j=0j=0 的時候 XiX_iXi? 和 YjY_jYj? 的最長公共子序列的長度。然后通過動態規劃法的公式得出
c(i,j)={0i=0,j=0c(i?1,j?1)i>0,j>0,xi=yjmax{c(i?1,j),c(i,j?1))}i>0,j>0,xi?=yjc(i,j)=\begin{cases}0 \quad \quad \quad \quad i=0,j=0 \\ c(i-1,j-1) \quad \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \quad i>0,j>0,x_i=y_j\\ max\{c(i-1,j),c(i,j-1))\} \quad i>0,j>0,x_i\not=y_j\end{cases} c(i,j)=??????0i=0,j=0c(i?1,j?1)????????????????????????i>0,j>0,xi?=yj?max{c(i?1,j),c(i,j?1))}i>0,j>0,xi???=yj??
然后我們通過公式計算 c(1,1)c(1,1)c(1,1) ,因為 x1x_1x1? 和 y1y_1y1? 不相等,得出 c(1,1)=max{c(0,1),c(1,0)}=0c(1,1)=max \{ c(0,1),c(1,0) \}=0c(1,1)=max{c(0,1),c(1,0)}=0 。然后依次計算,就會得到圖中的值,然后得出 xxx 和 yyy 的最長公共子序列的長度為4。我們在計算的時候會發現一個規律:當 xi=yjx_i=y_jxi?=yj? 的時候 c(i,j)c(i,j)c(i,j) 的值為左上角格子的數加1;當 xi?=yjx_i\not=y_jxi???=yj? 的時候 c(i,j)c(i,j)c(i,j) 的值為左側格子和上邊格子中的較大的一個。
代碼實現
import sysstr1 = sys.argv[1] str2 = sys.argv[2]len1 = len(str1) len2 = len(str2)maxChildLen = 0lcs_ss = [[0 for i in range(len2 + 1)] for j in range(len1 + 1)]for i in range(1, len1 + 1):for j in range(1, len2 + 1):if str1[i-1] == str2[j-1]:lcs_ss[i][j] = lcs_ss[i-1][j-1] + 1else:lcs_ss[i][j] = max(lcs_ss[i-1][j], lcs_ss[i][j-1])maxChildLen = lcs_ss[len1][len2]print("str1: %s" % str1) print("str2: %s" % str2) print("LCS: %s" % maxChildLen)隨便輸入兩個字符串,然后觀察打印結果
str1: acedbae str2: becadeac LCS: 3Process finished with exit code 0若有任何問題,懇請不吝指正。
歡迎關注公眾號:「努力給自己看」
總結
- 上一篇: 单页应用和多页应用
- 下一篇: ubuntu14.04自定义系统默认xp