第五十三天| 1143.最长公共子序列、1035.不相交的线、53. 最大子序和

2024-03-11 02:52

本文主要是介绍第五十三天| 1143.最长公共子序列、1035.不相交的线、53. 最大子序和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Leetcode 1143.最长公共子序列

题目链接:1143 最长公共子序列

题干:给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

思考:动态规划。

  • 确定dp数组(dp table)以及下标的含义

dp[i][j]:字符串text1的处理区间[0, i - 1]与字符串text2的处理区间[0, j - 1]中,存在的最长公共子序列的长度。

至于为什么要定义长度为[0, i - 1]的字符串text1,而不是定义为长度为[0, i]的字符串text1。

这主要是为了简化初始化操作,具体理由在 Leetcode 718. 最长重复子数组 中有讲解。

  • 确定递推公式

遍历过程就两种情况: text1[i - 1] 与 text2[j - 1]相同 以及 text1[i - 1] 与 text2[j - 1]不相同。

如果text1[i - 1] 与 text2[j - 1]相同,即找到一个公共元素,则在 text1[0, i - 2]与text2[0, j - 2]的最长公共子序列的末尾再加上此次找到的公共元素(即两字符串均缩小处理区间的结果加一)。因此dp[i][j] = dp[i - 1][j - 1] + 1;

如果text1[i - 1] 与 text2[j - 1]不相同,那就比较text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列(即任选一字符串缩小处理区间的结果),取最大的,则dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);

  • dp数组如何初始化

test1[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0;同理dp[0][j]也是0。

其他下标都是随着递推公式逐步覆盖,初始为多少都可以,所以可以统一初始为0。

  • 确定遍历顺序

从递推公式,可以看出,有三个方向可以推出dp[i][j],如图:

在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历矩阵。

  • 举例推导dp数组

举例:text1 = "abcde", text2 = "ace" ,dp状态如图:

代码:

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {//dp[i][j]:字符串text1的处理区间[0, i - 1]与字符串text2的处理区间[0, j - 1]中,存在的最长公共子序列的长度vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));     for (int i = 1; i <= text1.size(); i++) {           //遍历text1字符串for (int j = 1; j <= text2.size(); j++) {       //遍历text2字符串if (text1[i - 1] == text2[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[text1.size()][text2.size()];}
};

Leetcode 1035.不相交的线

题目链接:1035 不相交的线

题干:在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:

  •  nums1[i] == nums2[j]
  • 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

思考:动态规划。本题如果从怎么判断连线相交的角度思考会卡壳,如果将不相交理解为前后连线元素的相对顺序和数组的相对顺序一致,可以发现本题和上题一模一样。

直线不能相交,这就是说明在字符串A中 找到一个与字符串B相同的子序列,且这个子序列不能改变相对顺序,只要相对顺序不改变,链接相同数字的直线就不会相交。

因此本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度。

代码:

class Solution {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {//dp[i][j]: 数组nums1的处理区间[0, i - 1]与数组nums2的处理区间[0, j - 1]中,存在的最大连线数vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));for (int i = 1; i <= nums1.size(); i++) {           //遍历数组nums1for (int j = 1; j <= nums2.size(); j++){        //遍历数组nums2if (nums1[i - 1] == nums2[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[nums1.size()][nums2.size()];}
};

Leetcode 53. 最大子序和

题目链接:53 最大子序和

题干:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

思考:此题的贪心法解法:53. 最大子序和。下面为动态规划解法。

  • 确定dp数组(dp table)以及下标的含义

dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和。

  • 确定递推公式

从dp[i]的定义,可以看出dp[i]只有两个方向可以推出:

  • dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

并且要取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);

  • dp数组如何初始化

从递推公式可以看出来dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础。

根据dp[i]的定义,明显dp[0]应为nums[0]即dp[0] = nums[0]。

  • 确定遍历顺序

递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。

  • 举例推导dp数组

举例:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:

要找最大的连续子序列和,就应该比较每一个i为终点的连续最大子序列和。取其中最大值。

代码:

class Solution {
public:int maxSubArray(vector<int>& nums) {vector<int> dp(nums.size());        //dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和dp[0] = nums[0];int result = dp[0];       //记录最大和for (int i = 1; i < nums.size(); i++) {dp[i] = max(dp[i - 1] + nums[i], nums[i]);        //递推公式if (dp[i] > result) result = dp[i];}return result;}
};

自我总结:

        最长公共子序列和还没理解清晰。

这篇关于第五十三天| 1143.最长公共子序列、1035.不相交的线、53. 最大子序和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/796378

相关文章

poj3261(可重复k次的最长子串)

题意:可重复k次的最长子串 解题思路:求所有区间[x,x+k-1]中的最小值的最大值。求sa时间复杂度Nlog(N),求最值时间复杂度N*N,但实际复杂度很低。题目数据也比较水,不然估计过不了。 代码入下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring

poj1330(LCA最近公共祖先)

题意:求最近公共祖先 思路:之前学习了树链剖分,然后我就用树链剖分的一小部分知识就可以解这个题目了,记录每个结点的fa和depth。然后查找时,每次将depth大的结点往上走直到x = y。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

poj 3723 kruscal,反边取最大生成树。

题意: 需要征募女兵N人,男兵M人。 每征募一个人需要花费10000美元,但是如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱。 给出若干的男女之间的1~9999之间的亲密关系度,征募某个人的费用是10000 - (已经征募的人中和自己的亲密度的最大值)。 要求通过适当的招募顺序使得征募所有人的费用最小。 解析: 先设想无向图,在征募某个人a时,如果使用了a和b之间的关系

poj 3258 二分最小值最大

题意: 有一些石头排成一条线,第一个和最后一个不能去掉。 其余的共可以去掉m块,要使去掉后石头间距的最小值最大。 解析: 二分石头,最小值最大。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <c

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 2594 二分图最大独立集

题意: 求一张图的最大独立集,这题不同的地方在于,间接相邻的点也可以有一条边,所以用floyd来把间接相邻的边也连起来。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <sta