Uncrossed Lines

Updated on 30 June, 2025
Uncrossed Lines header image

Problem Statement

In this task, you are provided with two integer arrays, nums1 and nums2. The arrays are visualized as two separate horizontal lines where each integer in the arrays is depicted in the given order. The objective is to draw the maximum number of 'connecting lines' between the numbers on these two lines based on the following criteria:

  • A connecting line can only be drawn between nums1[i] and nums2[j] if nums1[i] == nums2[j].
  • These connecting lines must not intersect or overlap with one another; even at their endpoints, which means each number can participate in only one connecting line.

The challenge is to calculate the maximum number of such non-intersecting connecting lines that can be drawn between the two arrays.

Examples

Example 1

Input:

nums1 = [1,4,2], nums2 = [1,2,4]

Output:

2

Explanation:

We can draw 2 uncrossed lines as in the diagram.
We cannot draw 3 uncrossed lines, because the line from nums1[1] = 4 to nums2[2] = 4 will intersect the line from nums1[2]=2 to nums2[1]=2.

Example 2

Input:

nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]

Output:

3

Example 3

Input:

nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]

Output:

2

Constraints

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

Approach and Intuition

The task is fundamentally a problem of finding a maximum matching in a bipartite graph where each number in the arrays represents a node, and an edge can be drawn between nodes if their corresponding numbers are equal and do not lead to any crossed edges.

We can solve this using the concept from the Longest Common Subsequence (LCS) problem:

  1. The objective of drawing maximum non-intersecting lines between nums1 and nums2 where corresponding values are equal is similar to finding LCS, where the order matters and no two selected pairs can be in conflict. Each line can be thought of as a pairing (i, j) such that nums1[i] == nums2[j].

  2. We will use a dynamic programming table dp where dp[i][j] represents the maximum number of non-intersecting lines between the subarrays nums1[0...i-1] and nums2[0...j-1].

    • If nums1[i-1] == nums2[j-1], then adding these two to our solution might provide a better solution if this forms a part of a common subsequence. Thus, we might consider dp[i-1][j-1] + 1 for dp[i][j].
    • Otherwise, we should consider the best achievable without using either nums1[i-1] or nums2[j-1], which would be the max from either excluding the current element of nums1 or nums2, so dp[i][j] = max(dp[i-1][j], dp[i][j-1]).
  3. Begin with a base case where if either array is empty (i == 0 or j == 0), no lines can be drawn.

  4. Fill up the dp table iteratively, and the value in dp[length of nums1][length of nums2] will give the maximum number of such lines.

The solution thus obtained will ensure that the lines are drawn such that no two lines intersect, effectively solving the problem using dynamic programming based on the principles of finding the longest common subsequence.

Solutions

  • C++
  • Java
cpp
class Solution {
public:
    int maxNonCrossingLines(vector<int>& A, vector<int>& B) {
        int sizeA = A.size(), sizeB = B.size();
        vector<int> curr(sizeB + 1), last(sizeB + 1);
    
        for (int i = 1; i <= sizeA; i++) {
            for (int j = 1; j <= sizeB; j++) {
                if (A[i - 1] == B[j - 1]) {
                    curr[j] = 1 + last[j - 1];
                } else {
                    curr[j] = max(curr[j - 1], last[j]);
                }
            }
            last = curr;
        }
    
        return curr[sizeB];
    }
};

The provided C++ solution aims to solve the problem of finding the maximum number of non-crossing lines that can be drawn between the elements of two integer arrays. This problem essentially maps to finding the longest common subsequence (LCS) between the two arrays.

Here's a breakdown of the code's approach:

  • A solution class Solution is defined containing the function maxNonCrossingLines.
  • The function receives two integer vectors, A and B.
  • sizeA and sizeB store the sizes of vectors A and B respectively.
  • Two integer vectors, curr and last, each of size sizeB + 1, are used to store the current and the previous lengths of LCS up to each index.
  • The outer loop runs through each element in vector A, and the inner loop runs through each element in vector B.
  • During the traversal, if the elements A[i - 1] and B[j - 1] match:
    • curr[j] is set to 1 + last[j - 1], updating it to the length of LCS found so far including the current match.
  • If there is no match:
    • curr[j] is set to the maximum of the left (curr[j - 1]) and the upper values (last[j]) to ensure the longest sequence length is carried forward.
  • After each inner loop iteration, last is updated to the current values of curr to preserve the context for the next iteration.
  • Finally, the function returns curr[sizeB] which contains the length of the longest non-crossing lines (LCS) after considering all elements.

This algorithm efficiently computes the maximum number of non-crossing lines with a time complexity of O(n*m), where n and m are the sizes of vectors A and B, respectively, using a dynamic programming approach that uses two rolling arrays to keep the space complexity linear relative to the size of vector B.

java
class Solution {
    public int maximumUncrossedLines(int[] A, int[] B) {
        int lenA = A.length;
        int lenB = B.length;
    
        int[] current = new int[lenB + 1];
        int[] previous = new int[lenB + 1];
    
        for (int i = 1; i <= lenA; i++) {
            for (int j = 1; j <= lenB; j++) {
                if (A[i - 1] == B[j - 1]) {
                    current[j] = 1 + previous[j - 1];
                } else {
                    current[j] = Math.max(current[j - 1], previous[j]);
                }
            }
            previous = current.clone();
        }
            
        return current[lenB];
    }
}

The provided Java solution effectively tackles the problem of finding maximum number of uncrossed lines between two arrays, which can be visualized as a variant of the Longest Common Subsequence problem. Here’s a concise summary of how this code works:

  • Initialize two integer arrays, current and previous, sized one more than the length of array B (lenB + 1), to store intermediate results for dynamic programming.
  • Employ a nested loop where the outer loop iterates through each element of array A, and the inner loop iterates through each element of array B.
  • Inside the inner loop, check if the current elements of A and B, A[i-1] and B[j-1], are equal:
    • If they are, set current[j] to 1 + previous[j - 1] to build on the matched sequence.
    • If not, set current[j] to the maximum of current[j-1] or previous[j] to carry forward the maximum uncrossed lines count without extending the sequence.
  • After completing each inner loop iteration, copy the current array to the previous array with current.clone() to update the states for the next iteration.
  • The final result, after looping through both arrays, will be stored in current[lenB], which represents the maximum number of uncrossed lines between the two arrays.

This code utilizes dynamic programming efficiently by using only two arrays and updating them iteratively, minimizing space complexity while ensuring the optimal solution is computed.

Comments

No comments yet.