Uncrossed Lines
Medium · 1-D Dynamic Programming · Explanation and code example
Dynamic Programming (Optimal)
How it works
We can further optimize by using a single array and a variable to track the diagonal value from the previous iteration. This requires careful handling to avoid overwriting values we still need.
Steps
- If
nums2is longer thannums1, swap them so we iterate over the longer array in the outer loop. This minimizes space usage. - Initialize a 1D array
dpof sizem+1with zeros. - For each element in
nums1:- Track
prevto store the diagonal value before it gets overwritten. - For each element
jinnums2:- Save
dp[j+1]astempbefore updating. - If elements match,
dp[j+1] = 1 + prev. - Otherwise,
dp[j+1] = max(dp[j+1], dp[j]). - Update
prev = tempfor the next iteration.
- Save
- Track
- Return
dp[m].
Complexity
- Time complexity:
O(n * m) - Space complexity:
O(min(n, m))
Where
nandmare the sizes of the arraysnums1andnums2respectively.
Things to watch for
Not Recognizing This as LCS
Many struggle to identify that this problem is equivalent to finding the Longest Common Subsequence (LCS). The "uncrossed lines" constraint naturally maps to the subsequence property where relative order must be preserved. Failing to make this connection leads to overcomplicated solutions.
Off-by-One Errors in DP Table
When implementing the bottom-up DP solution, a common mistake is using incorrect indices. The DP table is typically sized (n+1) x (m+1) to handle base cases, but accessing nums1[i] vs dp[i+1] can be confusing. Ensure consistency between array indices and DP table indices.
Incorrect Pointer Movement in Space-Optimized DP
When using space-optimized DP with a single array, forgetting to track the previous diagonal value before overwriting leads to incorrect results. The prev variable must capture dp[j] before it gets updated, as this value is needed for the next iteration's diagonal lookup.
Python code example
from typing import List
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
n, m = len(nums1), len(nums2)
if m > n:
n, m = m, n
nums1, nums2 = nums2, nums1
dp = [0] * (m + 1)
for i in range(n):
prev = 0
for j in range(m):
temp = dp[j + 1]
if nums1[i] == nums2[j]:
dp[j + 1] = 1 + prev
else:
dp[j + 1] = max(dp[j + 1], dp[j])
prev = temp
return dp[m]
C++ code example
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
if (m > n) {
swap(nums1, nums2);
swap(n, m);
}
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; i++) {
int prev = 0;
for (int j = 0; j < m; j++) {
int temp = dp[j + 1];
if (nums1[i] == nums2[j]) {
dp[j + 1] = 1 + prev;
} else {
dp[j + 1] = max(dp[j + 1], dp[j]);
}
prev = temp;
}
}
return dp[m];
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.