《算法圖解》第9章 動態規劃 後面給出了怎麼求兩個字符串的 最長公共字串 和 最長公共子序列 的算法思路。但是沒有給出代碼實現,這裏根據其思路實現其算法python編程。
- 最長公共字串
爲兩個字符串a, b中相同的連續字符串的長度。
如 a=‘yhabcfdaefch’ , b=‘abcfaaegh’
則a,b的最長公共子串爲’abcf’
思路如下:
代碼如下:
import numpy as np
word_a='ABCEDFE'
word_b='AGHCDE'
len_a=len(word_a)
len_b=len(word_b) #獲取字符串a,b的長度
word_a=[word_a[i] for i in range(len_a)] #爲了後續方便計算,將字符串轉換成列表
word_b=[word_b[i] for i in range(len_b)]
cell=np.zeros((len_a+1,len_b+1),dtype=int) #考慮到迭代需要用到cell[i-1,j-1],
#爲了不超出範圍,因此設置網格大小爲:(len_a+1,len_b+1)
for i in range(1,len_a+1):
for j in range(1,len_b+1):
if word_a[i-1]==word_b[j-1]: #如果兩個字母相同
cell[i][j]=cell[i-1][j-1]+1 #值爲左上角鄰居的值加1
else:
cell[i][j]=0 #如果兩個字母不相同,值爲0
#到這一步已經完成了獲取最長公共子串長度的計算,但是還不知道最長公共子串是什麼
#以下幾行代碼的作用就是獲取最長公共字串
max_same_seq_len=np.max(cell) #獲取最長公共字串的長度
index=np.where(cell==max_same_seq_len) #最長公共字串的最後一個字符在cell上的位置
num_max_same_seq=len(index[0]) #獲取最長公共子串的數量
print('最長公共子串數量有: ',num_max_same_seq, '個')
for i in range(num_max_same_seq):
index_a=int(index[0][i]) #行表示該最長公共字串的最後一個字符在字符串a上的位置索引
max_same_seq=word_a[index_a-max_same_seq_len:index_a] #根據該最長子串的起始位置獲取該
#最長子串
#以下代碼等價
#index_b=int(index[1][i]) #列表示該最長公共字串的最後一個字符在字符串b上的位置索引
#max_same_seq=word_b[index_b-max_same_seq_len:index_b]
print('第',i, '個最長公共子串爲:',max_same_seq)
print()
根據上述代碼中的例子,運行結果如下:
- 最長公共子序列
如 a=‘yhabcfdaefhc’ , b=‘abcfaaegh’
則a,b的最長公共子序列爲’abcfaeh’
思路爲:
import numpy as np
word_a='ABCEFE'
word_b='ABHCEE'
len_a=len(word_a)
len_b=len(word_b) #獲取字符串a,b的長度
word_a=[word_a[i] for i in range(len_a)] #爲了後續方便計算,將字符串轉換成列表
word_b=[word_b[i] for i in range(len_b)]
#求最長公共子序列
grid=np.zeros((len_a+1,len_b+1),dtype=int) #考慮到迭代需要用到gridll[i-1,j-1],
#爲了不超出範圍,因此設置網格大小爲:(len_a+1,len_b+1)
for i in range(1,len_a+1):
for j in range(1,len_b+1):
if word_a[i-1]==word_b[j-1]: #如果兩個字母相同
grid[i][j]=grid[i-1][j-1]+1 #值爲左上角鄰居的值加1
else:
grid[i][j]=max(grid[i-1][j],grid[i][j-1]) #如果兩個字母不相同,就選擇上方和左方鄰居中交大的那個
max_seq_len=np.max(grid) #獲取最長公共字串的長度
max_seq=[] #最長公共字串列表
while max_seq_len:
index=np.where(grid==max_seq_len) #最長公共子序列長度對應的數值在grid上的位置
index_b=int(min(index[1])) #取該值對應的最小列的索引
max_seq.append(word_b[index_b-1]) #將索引位置對應的字符加入 max_seq裏面去
max_seq_len=max_seq_len-1 #最長公共子序列長度-1,以尋找下一個公共字符
max_seq=[max_seq[len(max_seq)-i-1] for i in range(len(max_seq))] #將確定的最長公共子序列逆序
print('最長公共子序列爲:',max_seq)
運行結果爲:
總結:這兩個算法的應用場景很多,如生物學家根據最長公共序列來確定DNA鏈的相似性,如指出兩個文件的差異性等。都屬於動態規劃的一種。而動態規劃中最重要的一個環節就是確定網格值的迭代公式。