力扣爆刷第80天--动态规划一网打尽子序列一维二维连续不连续变体
力扣爆刷第80天–动态规划一网打尽子序列一维二维连续不连续变体
零、总结
今天也是子序列的一天,但和上一期不同的是都是变体,也分为一维、二维、连续、非连续四种题型。
另外就是做子序列相关的题目一定要考虑好定义以后就使用实例自己推导一下,把结果展现出来,然后再看看能否推导出递推公式或者看看递推公式是否匹配。
一、1035.不相交的线
题目链接:https://leetcode.cn/problems/uncrossed-lines/description/
思路:本题不相交的线其实是求最长重复子序列,只是把题目的问法稍微改动了一点,但本质没有变,子序列具体的四种类型上一期有讲,本期不再赘述。
定义dp[i][j]表示区间nums1[0, i]和区间nums2[0, j]中以nums1[i]和nums2[j]为结尾的最长重复子序列的长度,那么如果nums1[i]==num2s[j],dp[i][j]自然可以从dp[i-1][j-1]推出,为dp[i][j] = dp[i-1][j-1] + 1;
如果nums1[i] != num2s[j],那么最长的长度要延续之前的长度,dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int[][] dp = new int[nums1.length+1][nums2.length+1];
for(int i = 1; i <= nums1.length; i++) {
for(int j = 1; j <= nums2.length; j++) {
if(nums1[i-1] == nums2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
}else{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[nums1.length][nums2.length];
}
}
二、53. 最大子序和
题目链接:https://leetcode.cn/problems/maximum-subarray/
思路:本题求的是最大和的连续子数组,是一维的,和上一期中的最长连续递增子序列是一类题目,只不过算个变体,改变了问法,但解题的方法没有变,依然是定义dp[i]表示为nums[0, i]中以nums[i]为结尾的连续子数组的最大和,那么对于每一个元素来说,是否加到上一个连续子数组的结尾,取决于加上后,和的值是否变大,不变大就另起炉灶,由此可以得到递推公式:dp[i] = Math.max(nums[i], dp[i-1]+nums[i]);
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++) {
dp[i] = Math.max(nums[i], dp[i-1]+nums[i]);
max = Math.max(max, dp[i]);
}
return max;
}
}
贪心:
另外本题使用贪心也可以做,求最大连续子数组的和,只要连续子数组的和大于0,就可以相加,如果小于0,便新开一个子数组。
class Solution {
public int maxSubArray(int[] nums) {
int sum = 0, max = Integer.MIN_VALUE;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
max = Math.max(sum, max);
if(sum < 0) {
sum = 0;
}
}
return max;
}
}
三、392.判断子序列
题目链接:https://leetcode.cn/problems/is-subsequence/
思路:本题是判断s是否是t的子序列,那么也有是一个变体,即s是连续,t非连续,上一期的是s与t都连续,或者都不连续,且看本题如何解决。
定义dp[i][j]表示,s[0, i] 和 t[0, j]中以s[i]为结尾,且以t[j]为结尾,是否为其子序列,如果s[i]==t[j]根据定义,延续dp[i-1][j-1]的状态,如果s[i] != t[j],应该延续,s[i]与t[j-1]的状态,如s = “b”, t = “abc” , 求dp[1][3],b与c不等,应该继承 “b” 与 "ab"的状态。
class Solution {
public boolean isSubsequence(String s, String t) {
if(s.length() > t.length()) return false;
if(s.length() == 0) return true;
boolean[][] dp = new boolean[s.length()+1][t.length()+1];
Arrays.fill(dp[0], true);
for(int i = 1; i <= s.length(); i++) {
for(int j = 1; j <= t.length(); j++) {
if(s.charAt(i-1) == t.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j] = dp[i][j-1];
}
}
}
return dp[s.length()][t.length()];
}
}
双指针:
下面的写法丑陋了一些,但也是双指针,调整了t的遍历位置。
class Solution {
public boolean isSubsequence(String s, String t) {
int len = 0, k = 0;
for(int i = 0; i < s.length(); i++) {
for(int j = k; j < t.length(); j++) {
if(s.charAt(i) == t.charAt(j)) {
len++;
k = j+1;
break;
}
}
}
return len == s.length();
}
}
四、115.不同的子序列
题目链接:https://leetcode.cn/problems/distinct-subsequences/
思路:本题也是变体,s不连续,t连续,和上一题是类似的,但是求的目标变成了组合数,依然定义为dp[i][j]表示为s[0, i]和t[0, j]以s[i]和t[j]为结尾,t出现在s中的组合数,那么当s[i]==t[j]时,不光可以记录下s[i-1]和t[j-1]的组合数,也可以记录下s[i-1]和t[j]的组合数,不等时,只需要记录s[i-1]与t[j]的组合数,具体可以简单推导一下。
class Solution {
public int numDistinct(String s, String t) {
int[][] dp = new int[s.length()+1][t.length()+1];
for(int i = 0; i < s.length(); i++) {
dp[i][0] = 1;
}
for(int i = 1; i <= s.length(); i++) {
for(int j = 1; j <= t.length(); j++) {
if(s.charAt(i-1) == t.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
}else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[s.length()][t.length()];
}
}
原文地址:https://blog.csdn.net/qq_43511039/article/details/136282687
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!