一、题目
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。
现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
二、思路
1)暴力法
暴力法的思路主要是通过循环找到所有解中最大的解值,通过两层循环+递归的方式(因为nums1数字就两种状态,连线or不连线),就假定backTrace函数是求出[loc1, nums1.length)与[loc2, nums2.length)的最多不相交线数量。
2)动态规划
暴力法重复计算了后续的连线方式,可以考虑使用动态规划记忆化状态。
本题可以化为求最长公共子序列问题,可以使用一个dp数组,dp[m][n]代表从nums1的[0, m]和nums2的[0, n]区间最多连线数量,那么dp[m][n]是依赖于之前的状态的(dp[m-1][n-1]、dp[m][n-1]、dp[m-1][n]),可以写出如下状态转移方程:
三、代码
1)暴力法
class Solution {
private int[] nums1;
private int[] nums2;
public int maxUncrossedLines(int[] nums1, int[] nums2) {
this.nums1 = nums1;
this.nums2 = nums2;
return backTrace(0, 0);
}
public int backTrace(int loc1, int loc2) {
int ans = 0;
for (int i = loc1; i < nums1.length; i++) {
for (int j = loc2; j < nums2.length; j++) {
if (nums1[i] == nums2[j]) {
ans = Math.max(ans, backTrace(i+1, j+1) + 1);
break;
}
}
}
return ans;
}
}
2)动态规划
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].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[dp.length-1][dp[0].length-1];
}
}
nums1长度为m,nums2长度为n,时间复杂度为O(mn),空间复杂度为O(mn)。
进一步降低时间复杂度:
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int[] dp = new int[nums2.length + 1];
for (int i = 0; i < nums1.length; i++) {
int[] temp = new int[nums2.length + 1];
for (int j = 1; j < dp.length; j++) {
if (nums1[i] == nums2[j-1]) {
temp[j] = dp[j-1] + 1;
} else {
temp[j] = Math.max(dp[j], temp[j-1]);
}
}
dp = temp;
}
return dp[dp.length-1];
}
}
时间复杂度为O(mn),空间复杂度为O(n)。