1035.不相交的线(中等) | Java刷题打卡

395 阅读3分钟

本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接

题目描述

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/un…

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

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

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

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

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

示例 1:

img

**输入:**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

提示:

  • 1 <= nums1.length <= 500
  • 1 <= nums2.length <= 500
  • 1 <= nums1[i], nums2[i] <= 2000

题目分析

这道题初看好像有点麻烦,其实就是最长公共子序列的变形

如果不相交的线有k条,那么nums1中的这k个点与nums2中的k个点,它们的相对顺序是一致的,所以它跟求"abcde""ace"这两个字符串的最长公共子序列(最长公共子序列是"ace")是一样一样的

使用动态规划来解,动态规划其实就是记住之前得到的答案,动态规划三步走

  • 分解为子问题 动态规划其实就是记住之前得到的答案,对于长度为mn的两个序列nums1nums2,如果nums1的第m项等于nums2的第n项,那么最长公共子序列就是前m-1n -1项的最长公共子序列加1,所以问题就转化为了求长度为m-1n-1的两个序列的最长子序列,这样就把问题分解了;同时,如果第m项不等于第n项,此时的最大公共子序列是第m项与第n-1项和第m-1项与第n项中更大的一个
  • 状态定义 dp[i][j]表示nums1中前i项与nums2j项的最长公共子序列
  • 状态方程推导 根据上边的分析已经能得到状态转移方程了,如果nums[i] = nums2[j],那么dp[i][j] = dp[i-1][j-1] + 1;如果nums[i] != nums2[j],那么dp[i][j] = max*(dp[i][j-1], dp[i -1][j]);
public class Solution {
  public int maxUncrossedLines(int[] nums1, int[] nums2) {
    // dp[i][j] = dp[i-1][j-1] +1
    //dp[i][j] = max(dp[i-1][j],dp[i][j-1])
    int m = nums1.length;
    int n = nums2.length;
    int[][] dp = new int[m + 1][n + 1];
    for (int i = 1; i <= m; i++) {
      for (int j = 1; j <= n; 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[m][n];
  }
}

总结

两层循环时间复杂度为O(mn),空间复杂度o(mn)

我把动态规划分为三步,分解为子问题状态定义状态方程推导

其关键在于记住之前得到的答案,因为后一步依赖于前一步的结果,必须要把前边的结果记下来,通常使用一个一维数组或者二维数字