经典算法——动态规划入门实例
来源:互联网 发布:去男朋友的城市 知乎 编辑:程序博客网 时间:2024/06/02 05:31
经典算法——动态规划入门实例
- 经典算法动态规划入门实例
- 神马是动态规划
- 求解最长公共子串
- 求解最大公有子序列
- 总结
- 经典算法动态规划入门实例
神马是动态规划
专业定义:
动态规划的本质,是对问题状态的定义和状态转移方程的定义。通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
看完之后或许你一个字也没记住,这都是什么鬼?
关键点
ok,让我们摒弃这些专业的条条框框,直奔主题。其实动态规划的核心就是拆解子问题——把一个大的问题拆解成逐步逐步的小问题,而这些小问题都可以直接由之前更小的问题得到。那么怎么拆解这些小问题呢?
靠的就是状态的定义和状态转移方程的定义。
当然,这样还是很抽象的。不如举个栗子:
求解最长公共子串
神马是最长公共子串?
假定现在有两个字符串,最长公共子串就是它们之间公有的连续的最长子串,就像下图这样
橙色部分就是最长的公有子串,记住这里一定要连续。那么问题来了?怎么求出任意两个字符串的最长公有子串呢?看似很简单,不过细思又感觉有点点复杂的感觉。
暴力求解
简单粗暴的方法——先求出str1的所有子串,然后在求出str2的所有子串,然后再一个个比较,找到相同且最大的那个不就好了。ok,我们先来看看要多少次:
str1的可能情况:
n+(n-1)+(n-1)+.......+2+1 = ((n+1)*n)/2
同理str2也是一样:
m+(m-1)+(m-1)+.......+2+1 = ((m+1)*m)/2
然后在通过两个for循环一个一个比较就搞定了嘛;
for i in range(((n+1)*n)/2): for i in range(((m + 1) * m) / 2): dosomthing()
ok,这样不就搞定了,so easy,不过感觉有点细思极恐,这也太暴力了,万一字符串很大,这得算多久呀!心疼计算机3s。正所谓暴力膜法不可取,所以这时候我们的动态规划就登场啦。
最开始我们说过,用动态规划算法时最重要的就是要拆解子问题,那么我们如何将这个问题拆解为更小的子问题呢?根据动态规划的思想,我们首先要分析出如何根据已有的结果算出我们需要的结果,举栗来说:
假如我们已经求好了截至到str1[i-1]与str2[j-1]处两个字符串的最大公有子串,是否可以帮助我们求出截止到str1[i]与str[j]处两个字符串的最大公有子串呢?思考一下:
为了方便,我们将截止到(这里的截止到要包含str1[i]与str2[j])str[i]与str[j]处的最大字符串长度记作
lcs(i,j)
,假如:str1[i] == str2[j],那么我们直接在lcs(i-1,j-1)后面加1不就是lcs(i,j)了嘛,如果str1[i] !=str2[j],最大公有子串到这里就结束了,所以经过这两个点没有最大公有子串,直接记作0就好了。用数学公式就是:
f(m,n)=0 str1[m] != str2[n] ;f(m,n)=f(m-1,n-1) + 1 str[m]==str2[n];
转化为代码就是:
if str1[i]==str2[j]: lcs[i][j] = lcs[i-1][j-1] + 1else: lcs[i][j] = 0
用一张图片描述整体流程就是:
ok,既然弄清楚了计算的流程,就可以写出代码,完整代码如下:
def lcs(mes1, mes2): if len(mes1) > len(mes2): mes1, mes2 = mes2, mes1 data = [[0 for i in range(len(mes2) + 1)] for i in range(len(mes1) + 1)] mes1 = "$" + mes1 mes2 = "$" + mes2 max_lenth = 0 end = 0 for i in range(1, len(mes1)): for j in range(1, len(mes2)): if mes1[i] == mes2[j]: data[i][j] = data[i - 1][j - 1] + 1 if data[i][j] > max_lenth: max_lenth = data[i][j] end = i if max != 0: result = mes1[end - max_lenth + 1:end + 1] print(result)if __name__ == '__main__': while True: try: mes1 = input() mes2 = input() lcs(mes1, mes2) except: break
这里申请的二维数组比两个字符串都要大1,原因为我们呢将i,j为0的边都赋值为了0(当然,这里为了方便直接把数组都初始化了为0),有点像为我们的动态规划棋盘撒了半圈水雷一样。参考上面的流程图。这是一道经典的动态规划题目,在华为的笔试题里面曾经出现过,上面代码就是之前根据题目写的,已经通过了测试。原题如下:
求解最大公有子序列
神马是最大公有子序列
乍一看,和最大公共子串就两字之差,其实这两个差的还是挺远的。还是上面那两个字符串:
橙色部分就是他们的最大公有子序列。和子串最大的区别就是这里的子串可以不用连续了,所以叫最大子序列了。这种算法有什么用呢?
比如:有次毛概老师让你写一篇5000字的论文,可是要交的前一天晚上你才想起来怎么办?当然是网上随便找一篇,但是你又怕老师发现是抄袭的,所以就会在文章开头改点东西,文章中间改点,文章结尾改点,当你以为万事大吉的时候,如果老师刚好学过这个算法,那么可以通过对比最大子序列找到文章的相似度了,然后就。。。。
暴力求解
。。。。。。。。。。。。这个子集比之前的高多了,有兴趣朋友可以计算试试。。。。。。。
其实仔细想想,这个和最大子串其实是一样一样的,就是递推公式有一丢丢不同,希望大家稍微思考下,应该很快就写出来了。完整代码如下,建议对比之前代码就会发现差别了:
def lcs(mes1, mes2): if len(mes1) > len(mes2): mes1, mes2 = mes2, mes1 data = [[0 for i in range(len(mes2) + 1)] for i in range(len(mes1) + 1)] mes1 = "$" + mes1 mes2 = "$" + mes2 max_lenth = 0 end = 0 for i in range(1, len(mes1)): for j in range(1, len(mes2)): if mes1[i] == mes2[j]: data[i][j] = data[i - 1][j - 1] + 1 else: data[i][j] = max(data[i - 1][j], data[i][j - 1]) i = len(mes1) - 1 j = len(mes2) - 1 str = "" while i != 0 and j != 0: if mes1[i] == mes2[j]: str += mes1[i] i -= 1 j -= 1 else: if data[i][j - 1] > data[i - 1][j]: j -= 1 else: i -= 1 print(str[::-1])if __name__ == '__main__': lcs("abcdacgw", "acdacvgwc")
其实就是拷贝的前面代码,改了几行代码和输出而已。
总结
- 算法是一门很理论通过也很实践的学问,建议先搞清流程,再写代码加深理解。本人算法菜鸟,如果错误,欢迎一起探讨。共同学习进步。
- 经典算法——动态规划入门实例
- 嵌套模型(DAG上的动态规划)—动态规划入门(算法经典入门)
- 嵌套模型(DAG上的动态规划)—动态规划入门(算法经典入门)
- 经典算法之—动态规划
- 硬币问题 动态规划 算法入门经典
- 经典算法之动态规划(一):入门级动态规划
- 动态规划经典入门
- 0-1背包问题 动态规划 算法入门经典
- 嵌套矩形 DAG上的动态规划 算法入门经典
- 算法竞赛入门经典---函数部分4(动态规划)
- 算法竞赛入门经典 DAG上的动态规划
- 算法入门(2)——动态规划算法
- 动态规划算法入门
- 动态规划算法入门
- 动态规划经典入门题目
- 背包问题——动态规划算法详解与实例
- 经典算法之-----动态规划
- 动态规划算法经典案例
- 面试笔记3
- log4j.properties 详解与配置步骤
- JS的基本类型
- PAT (Basic Level) Practise (中文) 1035. 插入与归并(25)
- 过滤器生命周期-17
- 经典算法——动态规划入门实例
- CLANG技术分享系列二:代码风格检查(A CLANG PLUGIN APPROACH)
- 输入框圆角
- Java程序设计基础篇(第10版)习题5.17
- mysql实现一个定时刷新用户登录记录的中间库
- P
- PostgreSQL外部表应用
- EL表达式
- springmvc与struts2不同