718. Maximum Length of Repeated Subarray
Problem Description
You are given two integer arrays nums1
and nums2
. Your task is to find the maximum length of a subarray that appears in both arrays.
A subarray is a contiguous sequence of elements within an array. For example, if nums1 = [1,2,3,4]
, then [2,3]
is a subarray of nums1
.
The problem asks you to find the longest common subarray that exists in both nums1
and nums2
. The subarray must be exactly the same in both arrays (same elements in the same order) and must be contiguous.
For example:
- If
nums1 = [1,2,3,2,1]
andnums2 = [3,2,1,4,7]
, the longest common subarray is[3,2,1]
with length 3 - If
nums1 = [0,0,0,0,0]
andnums2 = [0,0,0,0,0]
, the longest common subarray is[0,0,0,0,0]
with length 5
The solution uses dynamic programming where f[i][j]
represents the length of the longest common subarray ending at position i-1
in nums1
and position j-1
in nums2
. When nums1[i-1] == nums2[j-1]
, we extend the previous common subarray by 1, otherwise the length is 0. The answer is the maximum value found in the entire DP table.
Intuition
The key insight is recognizing that this is a classic dynamic programming problem similar to finding the longest common substring between two strings.
When we look for common subarrays, we need to track contiguous sequences. If we find matching elements at positions i
and j
in the two arrays, we want to know: can we extend a previous common subarray that ended just before these positions?
This leads us to think about building up solutions from smaller subproblems. If nums1[i] == nums2[j]
, and we already know the length of the longest common subarray ending at nums1[i-1]
and nums2[j-1]
, then we can extend that length by 1.
The dynamic programming approach emerges naturally from this observation:
- If elements match at positions
i
andj
, the length of common subarray ending here is1 + length of common subarray ending at (i-1, j-1)
- If elements don't match, we can't have a common subarray ending at these positions, so the length is 0
We create a 2D table f[i][j]
where each cell represents the length of the longest common subarray ending exactly at position i-1
in nums1
and j-1
in nums2
. By computing all possible ending positions and keeping track of the maximum length found, we get our answer.
The reason we use indices i-1
and j-1
instead of i
and j
directly is to handle the base case cleanly - when there's no previous element, the DP table has 0s at the boundaries, which correctly represents that there's no common subarray to extend from.
Learn more about Binary Search, Dynamic Programming and Sliding Window patterns.
Solution Approach
The solution implements a bottom-up dynamic programming approach using a 2D table to find the maximum length of common subarrays.
Data Structure:
- A 2D DP table
f
of size(m+1) × (n+1)
wherem = len(nums1)
andn = len(nums2)
- Each cell
f[i][j]
stores the length of the longest common subarray ending atnums1[i-1]
andnums2[j-1]
Algorithm Steps:
-
Initialize the DP table: Create a 2D array
f
with dimensions(m+1) × (n+1)
filled with zeros. The extra row and column handle the base cases where we have empty subarrays. -
Fill the DP table: Iterate through each position in both arrays:
for i in range(1, m + 1): for j in range(1, n + 1):
-
Check for matching elements: At each position
(i, j)
, comparenums1[i-1]
withnums2[j-1]
:- If they match:
f[i][j] = f[i-1][j-1] + 1
- This extends the common subarray that ended at the previous positions
- If they don't match:
f[i][j]
remains 0 (already initialized)- No common subarray can end at these mismatched positions
- If they match:
-
Track the maximum: After each update where elements match, update the answer:
ans = max(ans, f[i][j])
-
Return the result: The variable
ans
holds the maximum length found across all possible ending positions.
Time Complexity: O(m × n)
where m
and n
are the lengths of the two arrays, as we fill each cell of the DP table once.
Space Complexity: O(m × n)
for the 2D DP table.
The beauty of this approach is that it systematically considers all possible ending positions for common subarrays and builds up the solution incrementally, ensuring we find the global maximum.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with nums1 = [1,2,3,2,1]
and nums2 = [3,2,1,4]
.
Step 1: Initialize the DP table
We create a table f
of size (6 × 5)
filled with zeros. The indices represent:
- Rows 0-5 for
nums1
(with row 0 as padding) - Columns 0-4 for
nums2
(with column 0 as padding)
Initial table:
0 1 2 3 4 - 3 2 1 4 0 - 0 0 0 0 0 1 1 0 0 0 0 0 2 2 0 0 0 0 0 3 3 0 0 0 0 0 4 2 0 0 0 0 0 5 1 0 0 0 0 0
Step 2: Fill the table row by row
For i=1, j=1
: Compare nums1[0]=1
with nums2[0]=3
→ No match, f[1][1]=0
For i=1, j=2
: Compare nums1[0]=1
with nums2[1]=2
→ No match, f[1][2]=0
For i=1, j=3
: Compare nums1[0]=1
with nums2[2]=1
→ Match! f[1][3]=f[0][2]+1=1
, ans=1
Continuing this process for all cells:
When i=3, j=1
: nums1[2]=3
matches nums2[0]=3
→ f[3][1]=f[2][0]+1=1
When i=4, j=2
: nums1[3]=2
matches nums2[1]=2
→ f[4][2]=f[3][1]+1=2
, ans=2
When i=5, j=3
: nums1[4]=1
matches nums2[2]=1
→ f[5][3]=f[4][2]+1=3
, ans=3
Final DP table:
0 1 2 3 4 - 3 2 1 4 0 - 0 0 0 0 0 1 1 0 0 0 1 0 2 2 0 0 1 0 0 3 3 0 1 0 0 0 4 2 0 0 2 0 0 5 1 0 0 0 3 0
Step 3: Result
The maximum value in the table is 3, which corresponds to the common subarray [3,2,1]
:
- In
nums1
: positions 2,3,4 →[3,2,1]
- In
nums2
: positions 0,1,2 →[3,2,1]
The key insight: f[5][3]=3
tells us there's a common subarray of length 3 ending at nums1[4]
and nums2[2]
. By tracing back diagonally (from f[5][3]
to f[4][2]
to f[3][1]
), we can see how the common subarray was built up incrementally.
Solution Implementation
1class Solution:
2 def findLength(self, nums1: List[int], nums2: List[int]) -> int:
3 """
4 Find the length of the longest common subarray between two arrays.
5
6 Uses dynamic programming where dp[i][j] represents the length of the
7 longest common subarray ending at nums1[i-1] and nums2[j-1].
8
9 Args:
10 nums1: First integer array
11 nums2: Second integer array
12
13 Returns:
14 Length of the longest common subarray
15 """
16 # Get the lengths of both arrays
17 len1, len2 = len(nums1), len(nums2)
18
19 # Initialize DP table with dimensions (len1+1) x (len2+1)
20 # dp[i][j] stores length of common subarray ending at nums1[i-1] and nums2[j-1]
21 dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
22
23 # Track the maximum length found
24 max_length = 0
25
26 # Iterate through all positions in both arrays
27 for i in range(1, len1 + 1):
28 for j in range(1, len2 + 1):
29 # If elements match, extend the common subarray from previous diagonal
30 if nums1[i - 1] == nums2[j - 1]:
31 dp[i][j] = dp[i - 1][j - 1] + 1
32 max_length = max(max_length, dp[i][j])
33 # If elements don't match, common subarray length is 0 (implicit due to initialization)
34
35 return max_length
36
1class Solution {
2 /**
3 * Find the length of the longest common subarray between two arrays.
4 * Uses dynamic programming to track the longest common subarray ending at each position.
5 *
6 * @param nums1 First integer array
7 * @param nums2 Second integer array
8 * @return Length of the longest common subarray
9 */
10 public int findLength(int[] nums1, int[] nums2) {
11 int length1 = nums1.length;
12 int length2 = nums2.length;
13
14 // dp[i][j] represents the length of longest common subarray
15 // ending at nums1[i-1] and nums2[j-1]
16 int[][] dp = new int[length1 + 1][length2 + 1];
17
18 int maxLength = 0;
19
20 // Iterate through all positions in both arrays
21 for (int i = 1; i <= length1; i++) {
22 for (int j = 1; j <= length2; j++) {
23 // If elements match, extend the common subarray from previous position
24 if (nums1[i - 1] == nums2[j - 1]) {
25 dp[i][j] = dp[i - 1][j - 1] + 1;
26 // Update the maximum length found so far
27 maxLength = Math.max(maxLength, dp[i][j]);
28 }
29 // If elements don't match, dp[i][j] remains 0 (default value)
30 }
31 }
32
33 return maxLength;
34 }
35}
36
1class Solution {
2public:
3 int findLength(vector<int>& nums1, vector<int>& nums2) {
4 // Get the lengths of both input arrays
5 int m = nums1.size();
6 int n = nums2.size();
7
8 // Create a 2D DP table where dp[i][j] represents the length of
9 // common subarray ending at nums1[i-1] and nums2[j-1]
10 // Initialize with zeros (default constructor behavior)
11 vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
12
13 // Track the maximum length found so far
14 int maxLength = 0;
15
16 // Iterate through all possible positions in both arrays
17 for (int i = 1; i <= m; ++i) {
18 for (int j = 1; j <= n; ++j) {
19 // If elements at current positions match
20 if (nums1[i - 1] == nums2[j - 1]) {
21 // Extend the common subarray length from previous diagonal position
22 dp[i][j] = dp[i - 1][j - 1] + 1;
23 // Update the maximum length if current is larger
24 maxLength = max(maxLength, dp[i][j]);
25 }
26 // If elements don't match, dp[i][j] remains 0 (already initialized)
27 }
28 }
29
30 // Return the maximum length of common subarray found
31 return maxLength;
32 }
33};
34
1/**
2 * Finds the length of the longest common subarray between two arrays
3 * @param nums1 - First input array
4 * @param nums2 - Second input array
5 * @returns The length of the longest common subarray
6 */
7function findLength(nums1: number[], nums2: number[]): number {
8 // Get the lengths of both arrays
9 const length1: number = nums1.length;
10 const length2: number = nums2.length;
11
12 // Create a 2D DP table with dimensions (length1 + 1) x (length2 + 1)
13 // dp[i][j] represents the length of common subarray ending at nums1[i-1] and nums2[j-1]
14 const dp: number[][] = Array.from(
15 { length: length1 + 1 },
16 () => new Array<number>(length2 + 1).fill(0)
17 );
18
19 // Track the maximum length found
20 let maxLength: number = 0;
21
22 // Iterate through all possible positions in both arrays
23 for (let i = 1; i <= length1; i++) {
24 for (let j = 1; j <= length2; j++) {
25 // If elements at current positions match
26 if (nums1[i - 1] === nums2[j - 1]) {
27 // Extend the common subarray length from the previous diagonal position
28 dp[i][j] = dp[i - 1][j - 1] + 1;
29 // Update the maximum length if current subarray is longer
30 maxLength = Math.max(maxLength, dp[i][j]);
31 }
32 // If elements don't match, dp[i][j] remains 0 (already initialized)
33 }
34 }
35
36 return maxLength;
37}
38
Time and Space Complexity
Time Complexity: O(m * n)
where m
is the length of nums1
and n
is the length of nums2
. The algorithm uses two nested loops that iterate through all possible positions in the DP table. The outer loop runs m
times (from 1 to m+1
), and the inner loop runs n
times (from 1 to n+1
). Each cell computation takes O(1)
time, resulting in a total time complexity of O(m * n)
.
Space Complexity: O(m * n)
where m
is the length of nums1
and n
is the length of nums2
. The algorithm creates a 2D DP table f
with dimensions (m+1) × (n+1)
to store the lengths of common subarrays ending at each position. This requires O((m+1) * (n+1)) = O(m * n)
space. The additional variables ans
, i
, and j
use O(1)
space, so the overall space complexity is O(m * n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Index Confusion with DP Table Dimensions
One of the most common mistakes is confusing the indexing between the DP table and the original arrays. The DP table has dimensions (len1+1) × (len2+1)
, but the arrays are indexed from 0
to len-1
.
Pitfall Example:
# WRONG: Forgetting to subtract 1 when accessing array elements if nums1[i] == nums2[j]: # This will cause IndexError dp[i][j] = dp[i-1][j-1] + 1
Correct Approach:
# CORRECT: Remember dp[i][j] corresponds to nums1[i-1] and nums2[j-1] if nums1[i-1] == nums2[j-1]: dp[i][j] = dp[i-1][j-1] + 1
2. Space Optimization Oversight
While the 2D DP solution works correctly, it uses O(m×n) space which can be excessive for large arrays. Since we only need the previous row to compute the current row, we can optimize space.
Space-Optimized Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
len1, len2 = len(nums1), len(nums2)
# Use only two rows instead of full 2D table
prev = [0] * (len2 + 1)
curr = [0] * (len2 + 1)
max_length = 0
for i in range(1, len1 + 1):
for j in range(1, len2 + 1):
if nums1[i-1] == nums2[j-1]:
curr[j] = prev[j-1] + 1
max_length = max(max_length, curr[j])
else:
curr[j] = 0
prev, curr = curr, prev
return max_length
3. Forgetting to Reset Values When Elements Don't Match
When using space optimization or reusing arrays, failing to reset values when elements don't match can lead to incorrect results.
Pitfall Example:
# WRONG: Not resetting curr[j] when elements don't match
for j in range(1, len2 + 1):
if nums1[i-1] == nums2[j-1]:
curr[j] = prev[j-1] + 1
# Missing: else curr[j] = 0
This leaves old values in curr[j]
which can corrupt future calculations.
4. Misunderstanding the Problem - Subarray vs Subsequence
A critical mistake is confusing this problem with the Longest Common Subsequence (LCS) problem. Remember:
- Subarray: Must be contiguous
- Subsequence: Can skip elements
Wrong Approach (LCS logic):
# This finds longest common subsequence, NOT subarray
if nums1[i-1] == nums2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) # WRONG for subarray problem
For the subarray problem, when elements don't match, the length must be 0, not the maximum of adjacent cells.
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Want a Structured Path to Master System Design Too? Don’t Miss This!