You are given two integer arrays, A
and B
. You want to draw lines connecting elements from A
to elements from B
such that:
A[i]
to B[j]
if and only if A[i] == B[j]
.A
and B
can be connected at most once (no reusing elements).The task is to return the maximum number of lines you can draw under these constraints.
This problem is similar to finding the length of the Longest Common Subsequence (LCS) between A
and B
, since the lines cannot cross and elements cannot be reused.
At first glance, one might try every possible way to connect equal elements between A
and B
without crossing lines. However, this brute-force approach is highly inefficient because the number of possible combinations grows rapidly with the length of the arrays.
To optimize, we recognize that the problem is equivalent to finding the Longest Common Subsequence (LCS) between the two arrays. In LCS, we find the longest sequence that appears in both arrays in the same order (but not necessarily consecutively), and this naturally avoids crossings and reusing elements.
By mapping the problem to LCS, we can use dynamic programming to efficiently compute the answer.
We solve the problem using dynamic programming (DP), specifically the classic LCS technique. Here's how:
dp[i][j]
represent the maximum number of non-crossing lines (matches) we can draw between the first i
elements of A
and the first j
elements of B
.i == 0
or j == 0
, there are no elements to match, so dp[0][j] = 0
and dp[i][0] = 0
.A[i-1] == B[j-1]
, we can draw a line connecting them: dp[i][j] = dp[i-1][j-1] + 1
.A
or B
: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
.dp[len(A)][len(B)]
gives the maximum number of uncrossed lines.
We use a 2D array for the DP table, which is filled in row by row. This approach ensures that we consider all possible prefixes of A
and B
and always make the optimal choice at each step.
Let's consider the example: A = [1,4,2]
, B = [1,2,4]
.
dp
table of size 4 x 4
(since both arrays have length 3, add 1 for zero-based).i=1
, j=1
: A[0]=1
, B[0]=1
(match), so dp[1][1]=dp[0][0]+1=1
.
i=1
, j=2
: A[0]=1
, B[1]=2
(no match), so dp[1][2]=max(dp[0][2], dp[1][1])=1
.
dp[3][3]=2
, meaning the maximum number of uncrossed lines is 2.
A[0]=1
to B[0]=1
and A[2]=2
to B[1]=2
(or A[1]=4
to B[2]=4
).O(2^min(m, n))
, where m
and n
are the lengths of A
and B
.O(m * n)
, since we fill a table of size m x n
.O(m * n)
for the DP table. This can be reduced to O(min(m, n))
with further optimization (using only two rows).The Uncrossed Lines problem is a classic example of recognizing when a seemingly complex matching problem can be solved efficiently using dynamic programming. By mapping the problem to the Longest Common Subsequence, we avoid brute-force enumeration and achieve an elegant, efficient solution. The key insight is that non-crossing, non-reused matches correspond exactly to the LCS property, making this a textbook DP application.
class Solution:
def maxUncrossedLines(self, A, B):
m, n = len(A), len(B)
dp = [[0]*(n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if A[i-1] == B[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
class Solution {
public:
int maxUncrossedLines(vector<int>& A, vector<int>& B) {
int m = A.size(), n = B.size();
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (A[i-1] == B[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
};
class Solution {
public int maxUncrossedLines(int[] A, int[] B) {
int m = A.length, n = B.length;
int[][] dp = new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (A[i-1] == B[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];
}
}
var maxUncrossedLines = function(A, B) {
let m = A.length, n = B.length;
let dp = Array.from({length: m+1}, () => Array(n+1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (A[i-1] === B[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];
};