拋出問題:
假定字符串 s1 = 'BDCABA', s2 = 'ABCBDAB',求s1和s2的最大公共子序列。
問題分析:
我們想要求出s1和s2的最大公共子序列,我們可以用c(i,j)表示s1(i)和s2(j)最大公共子序列的長度,
假定c(i,j) = m,
如果:s1[ i ]和s2[ j ]相等,那么推出c(i,j) = c(i-1,j-1) + 1,
如果:s1[ i ] 和 s2[ j ]不相等,那么得到c(i,j) = max(c(i,j-1),c(i-1,j))
總結為公式就是:
畫成表格更清楚,也就是如下圖,表格的數即為當前的最大公共子序列的值,箭頭即為該結果是怎么得來的
例如第5行,第5列,因為第5列的c和第5行c相等,所以就等於,第4行,第4列的值+1等到2
得到了這個值列表,就可以通過回溯的方法,從最后一個值,倒着按照箭頭的方向,依次記錄每個相等的值(也就是方向是左上的值),得到的結果就是我們要求的最終結果
代碼實現:
# -*- coding:utf-8 -*- # 日期:2018/6/9 15:44 # Author:小鼠標 # 求最長公共子序列 from numpy import * s1 = 'BDCABA' s2 = 'ABCBDAB' def val_list(s1,s2): # 兩個字符串的長度 len_s1 = len(s1) + 1 len_s2 = len(s2) + 1 # 方向列表 direction_list = [] # 生成len_s2+1行len_s1+1列的全0列表 res = zeros((len_s2,len_s1)) direction = zeros((len_s2,len_s1)) # print(res_list) for i in range(0, len_s2-1): for j in range(0, len_s1-1): #判斷是否相等 if s1[j] == s2[i]: res[i + 1, j + 1] = res[i, j] + 1 # 1左上 2 上 3左 direction[i + 1, j + 1] = 1 else: if res[i + 1, j] > res[i, j + 1]: res[i + 1, j + 1] = res[i + 1, j] direction[i + 1, j + 1] = 3 else: res[i + 1, j + 1] = res[i, j + 1] direction[i + 1, j + 1] = 2 return res,direction res ,direction= val_list(s1,s2) #方向列表 1左上 2 上 3左 # [[0. 0. 0. 0. 0. 0. 0.] # [0. 2. 2. 2. 1. 3. 1.] # [0. 1. 3. 3. 2. 1. 3.] # [0. 2. 2. 1. 3. 2. 2.] # [0. 1. 2. 2. 2. 1. 3.] # [0. 2. 1. 2. 2. 2. 2.] # [0. 2. 2. 2. 1. 2. 1.] # [0. 1. 2. 2. 2. 1. 2.]] #最大子序列的值列表 # [[0. 0. 0. 0. 0. 0. 0.] # [0. 0. 0. 0. 1. 1. 1.] # [0. 1. 1. 1. 1. 2. 2.] # [0. 1. 1. 2. 2. 2. 2.] # [0. 1. 1. 2. 2. 3. 3.] # [0. 1. 2. 2. 2. 3. 3.] # [0. 1. 2. 2. 3. 3. 4.] # [0. 1. 2. 2. 3. 4. 4.]] #回溯 遞歸求出結果 global s_res s_res = '' def Lcs_fun(s,res_list,i,j): if res_list[i,j] == 3: Lcs_fun(s,res_list,i,j-1) elif res_list[i,j] == 2: Lcs_fun(s,res_list,i-1,j) elif res_list[i,j] == 1: Lcs_fun(s,res_list,i-1,j-1) global s_res s_res += s[i-1] else: return Lcs_fun(s2,direction,len(s2),len(s1)) print(s_res)
這塊很容易就看暈了,仔細看,應該能看懂