動態規划——最長公共子序列與最長公共子串 (含Python實現代碼)
-
英文名稱:
- 最長公共子序列 Longest Common Subsequence
- 最長公共子串 Longest Common Substring
-
主要區別:子串必須要連續,子序列不需要
- 舉例: a b c d e f b 和 a b c x y b z
- 最長公共子序列:a b c b
- 最長公共子串:a b c
- 舉例: a b c d e f b 和 a b c x y b z
最長公共子序列
- 相關符號:字符串 X 與 Y。Xi 表示X在第i位上的字符(在這里X的第一位表示為1而不是0),Yj 同理。
c[i][j]表示字符串X1..i與Y1..j的最長公共子序列的長度 - 遞推關系式
\[ c[i][j] = \left\{ \begin{array}{} 0 & {i = 0, j = 0}\\ c[i-1][j-1] + 1 & {X_i = Y_j}\\ MAX(c[i-1][j], c[i][j-1]) & {X_i \neq Y_j}\\ \end{array} \right. \]
- 參考代碼
def longest_common_subsequence(X: str, Y: str):
index_x = len(X)+1 # columns
index_y = len(Y)+1 # rows
c = [['']*index_y for _ in range(index_x)]
for i in range(index_x):
for j in range(index_y):
if i == 0 or j == 0:
c[i][j] = ''
continue
if X[i-1] == Y[j-1]:
c[i][j] = c[i-1][j-1] + X[i-1]
else:
if len(c[i-1][j]) > len(c[i][j-1]):
c[i][j] = c[i-1][j]
else:
c[i][j] = c[i][j-1]
return len(c[index_x-1][index_y-1]), c[index_x-1][index_y-1]
- 代碼說明: 此處的代碼在理論上做出了一些改動,二維數組c被用來存儲每個位置上存在的最長序列了,
即 c[i][j] 存儲的是一個字符串而非長度。在遞推關系式上,第二項(Xi = Yj 時) 改為 c[i-1][j-1] + Xi。
這樣最后既可以得到最大的長度,也可以知道最長的序列是什么。
最長公共子串
- 相關符號: 字符串 X 與 Y,角標i,j。意義同上。 c[i][j]表示字符串X1..i與Y1..j的匹配時的最長子串。 current_lcs 表示整個運行過程中遇到的最長子串
- 遞推關系式
\[ c[i][j] = \left\{ \begin{array}{} Null & {i = 0, j = 0 \ OR\ X_i \neq Y_i}\\ c[i-1][j-1] + X_i & {X_i = Y_j}\\ \end{array} \right. \]
- 參考代碼
def longest_common_substring(X: str, Y: str):
index_x = len(X) + 1 # columns
index_y = len(Y) + 1 # rows
c = [[''] * index_y for _ in range(index_x)]
current_lcs = ''
for i in range(index_x):
for j in range(index_y):
if i == 0 or j == 0 or X[i-1] != Y[j-1]:
c[i][j] = ''
continue
if X[i-1] == Y[j-1]:
c[i][j] = c[i-1][j-1] + X[i-1]
if len(c[i][j]) > len(current_lcs):
current_lcs = c[i][j]
return len(current_lcs), current_lcs