动态规划解最长公共子串、子序列 python3

动态规划:
通过将大问题拆分成独立的小问题,先解决小问题。转换成代码的思路:网格方法,每个单元格为一个小问题。

举例:背包问题。
背包有重量限制3kg,3件物品有价值、重量两个属性。问题:在限制重量内装价值最大的物品。
分析思路:
1. 全排列组合。所有的物品组合为2^3=8个,为[{},{A},{B},{C},{AB},{AC},{BC},{ABC}]。如果将所有组合的重量列出,运行时间O(2^n),物品数量多的时候,非常慢。
2. 动态规划。单元格cell[i,j]表示对应限制条件i,j下的最大价值,各限制条件已经列出在表格中。最后一格cell[ItemC,3kg]内的最大价值,即为背包问题的最终答案。

cell[i,j]1kg2kg3kg
Item A仅A可选,容量限制为1kg仅A可选,容量限制为2kg仅A可选,容量限制为3kg
Item BAB可选,容量限制为1kgAB可选,容量限制为2kgAB可选,容量限制为3kg
Item CABC可选,容量限制为1kgABC可选,容量限制为2kgABC可选,容量限制为3kg

填充完上述表格中的值后,可以归纳得到以下的计算公式:

根据上述公式就可以编写迭代的代码。

 

字符串比较问题也可以应用动态规矩,拆分成单个字母之间的比较。

最长公共子串:
比较两个字符串都包含的最长子串的长度。本例中的'vesista'和'hsis',拥有的最长公共子串为'sis'。
代码逻辑:利用dict生成一个7*4的表格cell,cell[1][2]表示截止到‘vi’和‘his’的最长公共子串。cell单元格内值的判断规律为:如果对应行列代表的两个字母相同,值为左上角邻居的值加1;如果两个字母不同,值为0。

# 最长公共子串 python3
word_a = "vesista"
word_b = "hsis"
def sim_count(word_a,word_b):
    cell = [ [ 0 for x in range(len(word_b)) ] for y in range(len(word_a)) ] #初始化网格,全部赋值0
    for i in range(len(word_a)):
        for j in range(len(word_b)): 
            if word_a[i] == word_b[j] and (i < 1 or j < 1):
                cell[i][j] = 1 #判断第一格的值
            elif word_a[i] == word_b[j]:
                cell[i][j]=cell[i-1][j-1]+1 #根据第一格累加
    sim_atline = max(cell)
    sim_last_word = max(sim_atline)
    sim_last_word_i = cell.index(sim_atline)
    for i in range(sim_last_word_i-sim_last_word+1,sim_last_word_i+1):
        print(word_a[i],end=",")
    return(cell,sim_last_word)

mat,sim_len = sim_count(word_a,word_b)
print(sim_len)
>> s,i,s,3

最长公共子序列:
按照最长公共子串的判断标准,如果比较'fish'和'fosh','fort'和'fosh',结果都是2,但明显'fosh'和'fish'更相近。这时候需要放宽连续子串的条件,变为公共子序列,即为两个字符串中共有的序列包含的字母数量之和。'fish'和'fosh'的最长公共子序列为3('f','sh'),而'fort'和'fosh'的结果是2。
代码逻辑:如果对应行列代表的两个字母相同,值为左上角邻居的值加1(与最长公共子串相同);如果两个字母不同,选择上方和左方邻居中较大的那个。利用dict生成表格,利于后面定位输出字符串。

# 最长公共子序列 python3
word_a = "fish"
word_b = "fosh" #最长公共子串为2,子序列为3
def sub_count(word_a,word_b):
    cell = {}
    for i in range(len(word_a)):
        cell[i]={}
        for j in range(len(word_b)):
            cell[i][j]=0 #初始化网格,全部赋值0
            if i < 1 or j < 1: #判断第一行,第一列的值
                if word_a[i] == word_b[j]:
                    if i==0 and j==0:
                        cell[i][j] = 1
                        print(word_a[i],end=",")
                    elif i == 0 and j>=1:
                        cell[0][j] = cell[0][j-1]+1
                        print(word_a[i],end=",")
                    else:
                        cell[i][0] = cell[i-1][0]+1
                        print(word_a[i],end=",")
                else:
                    if i==0 and j==0:
                        cell[i][j] = 0
                    elif i == 0 and j>=1:
                        cell[0][j] = cell[0][j-1]
                    else:
                        cell[i][0] = cell[i-1][0]
            else: #根据第一行、第一列累加
                if word_a[i] == word_b[j]:
                    cell[i][j]=cell[i-1][j-1]+1 
                    print(word_a[i],end=",")
                else:
                    cell[i][j]=max(cell[i-1][j],cell[i][j-1])
    sim_len = []
    for i in cell.values():
        for j in i.values():
            sim_len.append(j)
    return(cell,max(sim_len))
mat,sim_len = sub_count(word_a,word_b)
print(sim_len)
>> f,s,h,3

参考资料:算法图解

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值