2019独角兽企业重金招聘Python工程师标准>>>

适合动态规划(DP,dynamic programming)方法的最优化问题有两个要素:最优子结构和重叠子问题。

最优子结构指的是最优解包含的子问题的解也是最优的。

重叠子问题指的是每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。

所以可以用如下步骤来解决:

  1. 递归定义最优解计算公式
  2. 构造dp矩阵,按照公式逐步计算。

接下来我们分别在最长公共子串、最长公共子序列和01背包问题中演示如何应用上面的套路。

开始讨论之前要明白子串和子序列的区别在于:子串要求在原字符串中是连续的,而子序列则没有要求[1]。例如, 字符串 s1=abcde,s2=ade,则LCStr=de,LCSeq=ade。

其中LCStr表示Longest Common Substring 。LCSeq表示Longest Common Subsequence。

1 最长公共子串

问题定义

对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。 给定两个字符串A和B,同时给定两串的长度n和m。

测试样例:"1AB2345CD",9,"12345EF",7

返回:4

1.1 递归公式

假设有字符串x,y

f(i,j)表示x中以x[i]结尾的子串集合和y中以y[j]结尾的子串集合,两者交集中最长串的长度。(i和j都在合法范围内)

比如x="caba",以a[2]结尾的子串集合如下: { "b","ab","cab"}

当x[i]!=y[j]时,毫无疑问,交集为空集,此时f(i,j) = 0

当x[i]==y[j]时,f(i,j) = f(i-1,j-1) + 1

当i=0或者j=0时,f(i,j) = 0 //递归出口,边界情况

1.2 dp矩阵

有了递归公式就可以设计dp表格了,假设x="caba" , y="bab" , 二维数组dp[i][j]对应f(i,j)。当x[i]==y[j]时,dp[i][j]=dp[i-1][j-1]+1

从左到右,从上到下计算得到dp表格:

同样的本题的dp表格如下

1.3 AC代码

class LongestSubstring {
public:int findLongest(string A, int n, string B, int m) {//初始化n*m matrixvector<vector<int> > dp(n,vector<int>(m,0));int res=0;for(int i=0;i<n;i++)//x串{for(int j=0;j<m;j++)// y串{if(A.at(i)==B.at(j)){if(i==0||j==0)  dp[i][j]=1;else    dp[i][j]=dp[i-1][j-1]+1;// udpate maximumif(res<dp[i][j]) res=dp[i][j];}}}return res;}
};

2 最长公共子序列LCS

问题定义

对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度, 这里的最长公共子序列定义为:有字符串A的下标序列U1,U2,U3...Un和字符串B的下标序列V1,V2,V3...Vn,其中Ui < Ui+1,Vi < Vi+1。且A[Ui] == B[Vi]。

给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。

测试样例:"1A2C3D4B56",10,"B1D23CA45B6A",12

返回:6

2.1 递归公式

假设有字符串x和y, 这里我们用x[0,i]来表示x中下标为[0,i]的子串切片,对应于python中的x[0:i+1]。比如x="abcdea",那么x[0,1]就表示"ab"。

定义f(i,j)表示x[0,i]和y[0,j]之间的最长公共子序列。

毫无疑问我们又要想办法用f(i-1,j-1)来表示f(i,j)。

当x[i]==y[j]时,f(i,j) = f(i-1,j-1) +1

当x[i]!=y[j]时,f(i,j) = max( f(i-1,j) , f(i,j-1) )

同样的为保证f()参数合法,当参数小于0时,返回值为0。

当i<0或者j<0时,f(i,j)=0 //递归出口

这不难理解,比如f(0,0)实际上比较的是x[0]和y[0]两个字符的最长公共子序列,无非是0或者1。

而f(-1,2)中参数-1表示x取一个空串,y取y[0,2]。空串和任何字符串的LCS毫无疑问都是0

2.2 dp矩阵

以x="abcdea",y="aebcda"为例,构造dp表格如下:

2.3 AC代码

class LCS {
public:int findLCS(string A, int n, string B, int m) {vector<vector<int> > dp(n,vector<int>(m,0));for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(A[i]==B[j]){                    int t=0;if(i-1>=0 && j-1>=0) t=dp[i-1][j-1];dp[i][j]=t+1;}                    else{                    int t1=0;int t2=0;if(i-1>=0) t1=dp[i-1][j];if(j-1>=0) t2=dp[i][j-1];dp[i][j]=max(t1,t2);}}}//右下角return dp[n-1][m-1];}
};

这里需要考虑的边界情况较多,为简化代码,可以考虑字符串从1开始数

dp[i][j]就表示x[0,i-1]和y[0,j-1]的最长公共子序列

对应递归公式[1]:

这样的dp矩阵,字符串的下标和矩阵的行号列号会有些错位:

不需要判断越界问题,代码精简,但是不好理解

class LCS {
public:int findLCS(string A, int n, string B, int m) {//(n+1)*(m+1)vector<vector<int> > dp(n+1,vector<int>(m+1,0));       //按照dp的index来填充矩阵for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){                if(A[i-1]==B[j-1]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}return dp[n][m];}
};//或者class LCS {
public:int findLCS(string A, int n, string B, int m) {vector<vector<int> > dp(n+1,vector<int>(m+1,0));    //按照string的索引来填充dp矩阵for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(A[i]==B[j]) dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);}}return dp[n][m];}
};

3 01背包问题

问题定义[2]

话说有一哥们去森林里玩发现了一堆宝石,他数了数,一共有n个。 但他身上能装宝石的就只有一个背包,背包的容量为C。这哥们把n个宝石排成一排并编上号: 0,1,2,…,n-1。第i个宝石对应的价值和重量分别为V[i]和W[i] 。排好后这哥们开始思考: 背包总共也就只能装下体积为C的东西,那我要装下哪些宝石才能让我获得最大的利益呢?

背包问题分为01背包问题和部分背包问题,区别在于物品是否不可分割。

这里的物品不能分割,讨论的是01背包问题。

假设有1,2,3...n一共n个物品。其中v[i]表示第i个物品价值。w[i]表示第i个物品重量。

d(i,j)表示,要把前i个物品放入背包,背包容量为j

  • 1 递归公式:d(i,j)=

    • 0 //i is 0
    • max( d(i-1,j) , d(i-1,j-w[i])+v[i] ) //j>=w[i] and i>0
  • 2 列表计算

占位

#include<iostream>
#include <stdio.h>
#include<vector>
//#include"fsjtools.h"
using  namespace std;
int main()
{int n,c;//number,capwhile(cin>>n>>c){vector<int> w(n+1,0);//weightvector<int> v(n+1,0);//valuefor(int i=1;i<=n;i++) cin>>w[i]>>v[i];//start from 1//printVector(w);//printVector(v);vector<vector<int> > dp(n+1,vector<int>(c+1,0));//初始化为0for(int i=1;i<=n;i++){for(int j=0;j<=c;j++){if(j>=w[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]] + v[i]);else dp[i][j]=dp[i-1][j];}}cout<<dp[n][c]<<endl;}
}
样例输入5 10
4 9
3 6
5 1
2 4
5 14 9
4 20
3 6
4 20
2 45 10
2 6
2 3
6 5
5 4
4 6样例输出19
40
15

References

  1. DP:LCS(最长公共子串、最长公共子序列)
  2. 动态规划之背包问题(一)

转载于:https://my.oschina.net/SnifferApache/blog/754122

动态规划套路在最长公共子串、最长公共子序列和01背包问题中的应用相关推荐

  1. 【To Understand】动态规划:求最长公共子串/最长公共子序列

    动态规划:求最长公共子串/最长公共子序列 本博客转载自:https://blog.csdn.net/u013074465/article/details/45392687 该博客中详细讲解了求最长公共 ...

  2. 动态规划:最长公共子串 最长公共子序列

    一.最长公共子串 1. 题目 给定两个序列 X 和 Y,如果 Z 即是 X 的子串,又是 Y 的子串,我们就称它是 X 和 Y 的公共子串,注意子串是连续的. 例如 X = { A, B, C, D, ...

  3. 最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和...

    最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和 文章作者:Yx.Ac   文章来源:勇幸|Thinking (http://www.ahathi ...

  4. 动态规划示例汇总-Java版(组合硬币、跳台阶、最小路径和、最长递增子序列、最长公共子序列、01背包问题、最小编辑代价)

    动态规划算法示例汇总-Java版 组合硬币 Java解题-暴力搜索 Java解题-记忆搜索 Java解题-动态规划(两种写法) 跳台阶 Java解题-暴力递归 Java解题-动态规划 矩阵最小路径和 ...

  5. 【恋上数据结构】动态规划(找零钱、最大连续子序列和、最长上升子序列、最长公共子序列、最长公共子串、0-1背包)

    动态规划(Dynamic Programming) 练习1:找零钱 找零钱 - 暴力递归 找零钱 - 记忆化搜索 找零钱 - 递推 思考题:输出找零钱的具体方案(具体是用了哪些面值的硬币) 找零钱 - ...

  6. 最长公共子串问题-Java:解法一

    分享一个大牛的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击http://www.captainbed.net package live.every.day.Pro ...

  7. 采用顺序结构存储串,设计实现求串S和串T的一个最长公共子串的算法。

    算法分析 先固定字符串str1,取其第一个字符str1[0],(KMP算法)查找str1和str2中有没有以该字符开头的公共子串:即将str[0]与str2中的字符挨个比较,若遇到相等的,再接着比较s ...

  8. 最长公共子串LCS (Longest Common Subsequence) 算法

    三个方法都有所借鉴,但代码部分是自己试着写出来的,虽然最后的运行结果都是正确的,但此过程中难免会有考虑不周全的地方,如发现代码某些地方有误,欢迎指正.同时有新的想法,也可以提出! 采用顺序结构存储串, ...

  9. 动态规划解决01背包问题

    转自:https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html 一.问题描述:有n 个物品,它们有各自的重量和价值,现有给定容量的背包 ...

最新文章

  1. pyqtdeploy教程_PyQtdeploy-V2.4 User Guide 中文 (一)
  2. 如何修改PHP的memory_limit限制
  3. NSTimer与Run loop Modes
  4. ddos ***之 SYN Flood
  5. AtCoder AGC035D Add and Remove (状压DP)
  6. 国内初创企业选择云计算服务器价格对比
  7. scrapy 伪装代理和 fake_userAgent 的使用
  8. think route.php,ThinkPHP5—路由(route)详解
  9. markdown 书写代码
  10. HTML5七夕情人节表白网页制作【流星动画3D相册】HTML+CSS+JavaScript
  11. Spark性能优化之-shuffle调优
  12. 2018年ACM-ICPC亚洲区域赛(焦作)赛后总结
  13. 关于USIM卡的全面测试综述
  14. 74LVC245APW数据资料
  15. oracle数据库应用(2)
  16. qemu网络配置-桥接-IOT固件模拟
  17. solr6.4服务器+Tomcat+中文分词器
  18. Python Diary - Day 15 模块、异常和文件
  19. 手机突然间不能上网了,无线数据网络正常?
  20. Android阅读器——FolioReader

热门文章

  1. SS, SP, BP 三个寄存器的不同和应用
  2. 罚函数法求解约束问题最优解
  3. Sentinel 发布里程碑版本,添加集群流控功能
  4. Intellij IDEA Debug 调试技巧
  5. 【C++多线程系列】【七】实现经典的C/S架构
  6. asp.net中的<%%>形式的详细用法总结
  7. nginx 安装 虚拟主机
  8. SQL Server 文件路径
  9. hdu1466 计算直线的交点数
  10. SQL SERVER DBCC 命令集整理