最長公共子串 和 最長公共子序列 的python算法實現

《算法圖解》第9章 動態規劃 後面給出了怎麼求兩個字符串的 最長公共字串 和 最長公共子序列 的算法思路。但是沒有給出代碼實現,這裏根據其思路實現其算法python編程。

  1. 最長公共字串
    爲兩個字符串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()

根據上述代碼中的例子,運行結果如下:
在這裏插入圖片描述

  1. 最長公共子序列
    如 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鏈的相似性,如指出兩個文件的差異性等。都屬於動態規劃的一種。而動態規劃中最重要的一個環節就是確定網格值的迭代公式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章