1143. 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace” ,它的长度为 3 。
示例 2:
输入:text1 = “abc”, text2 = “abc”
输出:3
解释:最长公共子序列是 “abc” ,它的长度为 3 。
示例 3:
输入:text1 = “abc”, text2 = “def”
输出:0
解释:两个字符串没有公共子序列,返回 0 。
1.状态表示*
状态表⽰:
对于两个数组的动态规划,我们的定义状态表⽰的经验就是:
i. 选取第⼀个数组 [0, i] 区间以及第⼆个数组 [0, j] 区间作为研究对象;
ii. 结合题⽬要求,定义状态表⽰。
在这道题中,我们根据定义状态表⽰为:
dp[i][j] 表⽰: s1 的 [0, i] 区间以及 s2 的 [0, j] 区间内的所有的⼦序列中,最
⻓公共⼦序列的⻓度
2.状态转移方程
分析状态转移⽅程的经验就是根据「最后⼀个位置」的状况,分情况讨论。
对于 dp[i][j] ,我们可以根据 s1[i] 与 s2[j] 的字符分情况讨论:
- . 两个字符相同, s1[i] = s2[j] :那么最⻓公共⼦序列就在 s1 的 [0, i - 1] 以 及 s2 的 [0, j - 1] 区间上找到⼀个最⻓的,然后再加上 s1[i] 即可。因此 dp[i][j] = dp[i - 1][j - 1] + 1 ;
ii. 两个字符不相同, s1[i] != s2[j] :那么最⻓公共⼦序列⼀定不会同时以 s1[i] 和 s2[j] 结尾。那么我们找最⻓公共⼦序列时,有下⾯三种策略:
去 s1 的 [0, i - 1] 以及 s2 的 [0, j] 区间内找:此时最⼤⻓度为 dp[i - 1][j] ;
去 s1 的 [0, i] 以及 s2 的 [0, j - 1] 区间内找:此时最⼤⻓度为 dp[i ] [j - 1] ;
去s1 的 [0, i - 1] 以及 s2 的 [0, j - 1] 区间内找:此时最⼤⻓度为 dp[i - 1][j - 1]
我们要三者的最⼤值即可。但是我们细细观察会发现,第三种包含在第⼀种和第⼆种情况⾥⾯,但是我们求的是最⼤值,并不影响最终结果。因此只需求前两种情况下的最⼤值即可。
综上,状态转移⽅程为:
if(s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1 ; if(s1[i] != s2[j]) dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
3. 初始化
a. 「空串」是有研究意义的,因此我们将原始 dp 表的规模多加上⼀⾏和⼀列,表⽰空串。
b. 引⼊空串后,⼤⼤的⽅便我们的初始化。
c. 但也要注意「下标的映射关系」,以及⾥⾯的值要「保证后续填表是正确的」。
当 s1 为空时,没有⻓度,同理 s2 也是。因此第⼀⾏和第⼀列⾥⾯的值初始化为 0 即可保证后续填表是正确的.
4. 填表顺序
根据「状态转移⽅程」得:从上往下填写每⼀⾏,每⼀⾏从左往右
5. 返回值
返回 dp[m][n]
代码:
int longestCommonSubsequence(string text1, string text2) { int n=text1.size(); int m=text2.size(); vector<vector<int>> dp(n+1,vector<int>(m+1)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(text1[i-1]==text2[j-1]) { dp[i][j]=dp[i-1][j-1]+1; } else { dp[i][j]=max(dp[i][j-1],dp[i-1][j]); } } } return dp[n][m]; }
1035. 不相交的线
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。
现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
示例 2:
输入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
输出:3
示例 3:
输入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
输出:2
解法思路:
我们仔细分析一下题目,求不相交的两条线,也就是求的是两个数组中的最长的公共子序列,
详细题解已经写在上一题,所以不再赘述。
代码
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) { int n=nums1.size(); int m=nums2.size(); vector<vector<int>> dp(n+1,vector<int>(m+1)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(nums1[i-1]==nums2[j-1]) { dp[i][j]=dp[i-1][j-1]+1; } else { dp[i][j]=max(dp[i][j-1],dp[i-1][j]); } } } return dp[n][m]; }