《算法图解》第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链的相似性,如指出两个文件的差异性等。都属于动态规划的一种。而动态规划中最重要的一个环节就是确定网格值的迭代公式。