浅层理解动态规划及利用动态规划解决最长公共子串等问题


动态规划基本思想


动态规划的工作原理是先解决子问题,再逐步解决大问题。


用动态规划解决旅游规划问题


目前面对的问题是,有A、B、C、D、E五个地点想要去参观,每个地点的评分不同,花费的时间也不同,假设你有两天的时间,怎么样能够在两天内参观达到评分最多的参观路线呢?各地点的花费时间以及评分如下:

名胜 时间/天数 评分
A 0.5 7
B 0.5 6
C 1 9
D 2 9
E 0.5 8

动态规划可以看做是一个建立网格并且填写网格的过程,表格填好后问题也可解决。建立一个空白的网格如下:

0.5 1 1.5 2
A
B
C
D
E
    1. 填写A行
      这一行中,每一个网格只需要决定要不要去A地,填写当前网格可能的最高评分值,牢记目的是使评分最高,0.5代表只有0.5天时间,1代表有1天时间,以此类推,因此A行的网络填写如下,每个网格都选择去A地:
A行网格评分.png
  • 2.填写B行
    B行填写时每个网格可选A.B两地,目的同样是选择评分更高的选项,准则如下,首先确定是否选择B,如果选择B,计算选B之后的空闲时间,并与空闲时间列的上一行最高评分值相加,作为这一网格的最高评分,若和大于上一行同列的评分则作为这一网格最终的评分,否则不选择B,继承上一行的评分。


    B行网格评分.png

    如B行第一列,假设选择B,则正好用完0.5天得到评分6,与上一行的7相比较小,因此,不选择B,继承上行的结果;第二列则为选择B后剩余0.5天,查找0.5天列的上一行,评分最高为7,故该网格的最高评分为7+6=13,超过上一行7,最终确定填写为13。

  • 3.填写剩余行
    每多一行,可选择的地点就多一个,填网格的法则如上,不在赘述,最终的网格如下:
    由图可知最终选择ACE三地参观可获得最高的评分。


    最终网格.png

动态规划的注意事项:


1.动态规划的结果不会随网格的行顺序而改变;
2.在动态规划时每个网格只有两种选择,是or否,没有选择商品的一部分这种选项;
3.动态规划的每个子问题都必须是离散独立的,不能依赖于其他子问题。


动态规划查找最长公共子串


动态规划要将某个指标最大化,最长子串指的是两个单词中相同字母最多的字符串,例如red和reg的最长公共子串是re,利用动态规划解决该问题单元格的值为共同字符串长度值,横纵坐标分别为两个单词,最终的结果为单元格中的最大值而不是最后一个单元格的值。

  • 计算公式:
    1.如果两个字母不同,值为0;
    2.如果两个字母相同,值为左上角邻居的值加1;

举例,fish和fosh的最长公共子串的最长公共子串长度为2,是sh


最长公共子串.png
  • python 代码实现
#动态规划解决最长公共子串问题
def findCommenstr(str1,str2):
    cell = [[0 for x in range(len(str2))] for y in range(len(str1))]
    max_ = 0
    max_id = -1
    for i in range(len(str1)):
        for j in range(len(str2)):
            if str1[i]==str2[i] and (i < 1 or j < 1): #确定第一行或者第一列的值
                cell[i][j]=1
            elif str1[i]==str2[j]:   #如果相同,加上左上角的值
                cell[i][j]=cell[i-1][j-1]+1  
            if cell[i][j] > max_:
                max_ = cell[i][j]    #获取最大值
                max_id = i           #获取行号

    sub_str = []          
    for i in range(max_id-max_+1,max_id + 1):
        sub_str.append(str1[i])
    return sub_str,max_

a='fish'
b='fosh'
sub,str_len = findCommenstr(a,b)
print(''.join(sub),str_len)

动态规划查找最长公共子序列


最长公共子序列:两个读单词都有的序列包含的字母数,即将各个公共子串的长度相加。子串要求在原字符串中是连续的,而子序列则只需保持相对顺序一致,并不要求连续
举例fish和fosh的最长公共子序列是fsh,长度为3。

最长公共子序列.png
  • 计算公式
    1.如果两个字母不同,就选择上方和左方邻居中较大的;
    2.如果两个字母相同,就将当前单元格的值设置为左上方单元格的值加1。

  • 得出公共子序列
    通常的学习资料中只有如何计算出最长公共子序列长度,没有将子序列具体串得出,笔者通过自己的研究,r如有问题,还请指教,笔者认为得出子序列输出的步骤如下:
    1.得到网格最大值的位置,将最大值对应的字符添加到子序列集合,
    当该位置不是第一行或者第一列时,将该值与左上方的结果比较:
    如果左上邻居小于当前值,将左上的邻居加入子序列集合;
    否则,先将当前值pop出集合,再将左上邻居添加到子序列集合;
    依次向网格左上方移动,直到遍历完网格的位置。

python实现代码如下:

# chapter 9 动态规划解决两个单词最长公共子序列问题 
def findCommenstr(str1,str2):
    cell = [[0 for x in range(len(str2))] for y in range(len(str1))]
    max_ = 0
    max_id = -1
    flag = []
    max_id_x =-1
    max_id_y =-1
    for i in range(len(str1)):
        for j in range(len(str2)):
            if str1[i]==str2[j] and (i<1 or j < 1): #确定第一行第一列的值
                cell[i][j]=1
            elif str1[i]==str2[j]:                  #如果相同,加上左上角的值
                cell[i][j]=cell[i-1][j-1]+1
            else:
                cell[i][j]=max(cell[i-1][j],cell[i][j-1]) if i>0 else cell[i][j-1]
            if cell[i][j] > max_:
                max_ = cell[i][j]    #获取最大值
                max_id_x = i         #获取行号
                max_id_y = j
    
    if max_id_x !=-1:                        #如果没有公共子序列则flag为[]
        flag.append(str1[max_id_x])                     
    while (max_id_x > 0 and max_id_y > 0):   #如果有公共子序列,回溯确定公共子串 
        if cell[max_id_x-1][max_id_y-1] < cell[max_id_x][max_id_y] and cell[max_id_x-1][max_id_y-1]>0:
            flag.append(str1[max_id_x-1])
        elif cell[max_id_x-1][max_id_y-1] == cell[max_id_x][max_id_y] and cell[max_id_x-1][max_id_y-1] > 0:
            flag.pop(-1)
            flag.append('*-*')                  #分隔子串的标记
            flag.append(str1[max_id_x-1])      #如果相等先把当前的拿出然后再添加左上角值
        else:                                 #如果当前表格已经为0,则结束
            flag.reverse()
            return flag,max_            
        max_id_x-=1
        max_id_y-=1
                
    flag.reverse()
    return flag,max_

a='sish'
b='wosh'
s,str_len = findCommenstr(a,b)
print('Str1 is',a)
print('Str2 is',b)
print('The common string is ',s, ', And the length is ',str_len)    

代码测试结果:


最长子序列代码测试结果.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容