1035、不相交的线
题目
在两条独立的水平线上按给定的顺序写下 nums1
和 nums2
中的整数。
现在,可以绘制一些连接两个数字 nums1[i]
和 nums2[j]
的直线,这些直线需要同时满足:
-
nums1[i] == nums2[j]
- 且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4] 输出:2 解释:可以画出两条不交叉的线,如上图所示。 但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
代码
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
//和上一题最长公共子序列逻辑完全一样(字母、数字相对顺序相同)
int len1 = nums1.length;
int len2 = nums2.length;
//dp[i][j]表示nums1[0,i-1],nums2[0,j-1]范围的最大公共子序列长度
//i和j不一定的子序列的末尾元素
int[][] dp = new int[len1+1][len2+1];
for(int i=1; i <= len1; i++){
for(int j=1; j <= len2; 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[len1][len2];
}
}
53、最大子数组和
题目
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
代码(贪心)
class Solution {
public int maxSubArray(int[] nums) {
//贪心
int res = Integer.MIN_VALUE; //最大和结果
int sum = 0; //局部连续和
for(int i=0; i < nums.length; i++){
sum += nums[i];
//更新res为当前的最大和(必须写下面的if上面)
if(sum > res){
res = sum;
}
//前面的序列起到副作用
if(sum < 0){
sum = 0;
}
}
return res;
}
}
代码(动态规划)
class Solution {
public int maxSubArray(int[] nums) {
//动态规划
//dp[i]表示以nums[i]作为末尾元素的连续子序列最大和
int[] dp = new int[nums.length];
int res = nums[0]; //最终结果,初始化为第一个元素和
dp[0] = nums[0]; //dp初始化
for(int i=1; i < nums.length; i++){
//取dp[i-1] + nums[i]:dp[i-1]大于0,前面的序列加数字i
//取nums[i]:dp[i-1]小于0,只要数组i
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
//更新res为当前最大和
if(dp[i] > res){
res = dp[i];
}
}
return res;
}
}
392、判断子序列
题目
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
示例 1:
输入:s = "abc", t = "ahbgdc" 输出:true
示例 2:
输入:s = "axc", t = "ahbgdc" 输出:false
代码
class Solution {
public boolean isSubsequence(String s, String t) {
//原理还是最长公共子序列,最后判断res是否等于s的长度就行
int len1 = s.length(); //s长度
int len2 = t.length(); //t长度
//dp[i][j]表示[0,i-1]的text1与[0,j-1]的text2的最长公共子序列
int[][] dp = new int[len1+1][len2+1];
//初始化:默认dp[0][j],dp[i][0]全为0
//把dp多扩展一个行列,就不用处理text1[0]和text2[0]的初始化情况了
//变相的把初始化的计算和下面的递推合在一起
//遍历顺序
for(int i=1; i <= len1; i++){
for(int j=1; j <= len2; j++){
//递推公式,s[i-1]和t[j-1]相同
if(s.charAt(i-1) == t.charAt(j-1)){
dp[i][j] = dp[i-1][j-1] + 1; //在i-2和j-2的基础上长度+1
}
//s[i-1]和t[j-1]不相同
else{
//ab,ac取(ab,a)和(a,ac)两者的最大长度
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
//判断最长公共子序列是否等于s的长度
return dp[len1][len2] == s.length();
}
}
115.不同的子序列
题目
给你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。
示例 1:
输入:s = "rabbbit", t = "rabbit"输出:3 解释:
示例 2:
输入:s = "babgbag", t = "bag"输出:5
代码
class Solution {
public int numDistinct(String s, String t) {
int len1 = s.length();
int len2 = t.length();
//dp[i][j]表示s的前i个字符中,t的前j个字符中,s能出现dp[i][j]次t
int[][] dp = new int[len1+1][len2+1];
//第一列全为1
for(int i=0; i <= len1; i++){
dp[i][0] = 1;
}
//第一行除了dp[0][0]全为0
for(int j=1; j <= len2; j++){
dp[0][j] = 0;
}
//遍历顺序
for(int i=1; i <= len1; i++){
for(int j=1; j <= len2; j++){
//字母相同
if(s.charAt(i-1) == t.charAt(j-1)){
//以bag、babgbag为例
//d[i-1][j-1]:b在babgb中出现三次,ba在babgba中也出现3次(相同元素a在末尾次数)
//dp[i-1][j]:ba在babgb中出现一次,ba在babgba中也出现1次(相同元素ba在前面次数)
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]; //左上方+上方
}
//字母不相同
else{
//bag在b中出现0次,g!=a时,bag在ba中也出现0次
dp[i][j] = dp[i-1][j]; //上方
}
}
}
return dp[len1][len2];
}
}
总结
以示例2,bag+babgbag为例,把二维dp的状态转移表画出来,根据例子理解递推公式和初始化。直接考虑什么删除不删除的很难理解。