LeetCode 2657 - Find the Prefix Common Array of Two Arrays
The problem provides two arrays, A and B, each a permutation of integers from 1 to n. A permutation means each number from 1 to n appears exactly once in the array.
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Bit Manipulation
Solution
Problem Understanding
The problem provides two arrays, A and B, each a permutation of integers from 1 to n. A permutation means each number from 1 to n appears exactly once in the array. We are asked to compute a new array C, called the prefix common array, where C[i] represents the number of elements that appear in both A and B up to index i.
In simpler terms, for each position i, we look at the first i + 1 elements of both A and B and count how many numbers are present in both slices. The constraints ensure that n is at most 50, so solutions with quadratic time complexity are acceptable but we can optimize further.
Important edge cases include small arrays of length 1, arrays where elements align perfectly, or arrays where common elements appear late in the sequences. The problem guarantees no repeated elements and consistent lengths, so we do not need to handle invalid inputs.
Approaches
The brute-force approach is straightforward. For each index i, we extract the first i + 1 elements of both arrays and compute the intersection. The count of this intersection is then stored in C[i]. While correct, this approach is inefficient because each intersection operation scans up to i + 1 elements, resulting in O(n^2) time complexity.
The optimal approach leverages the fact that A and B are permutations of 1 to n. We can use a frequency array (or a hash set) to track which numbers have appeared in either array. As we iterate through A and B simultaneously, we update counts for each number. When a number appears for the second time (meaning it has appeared in both arrays), we increment the running count for C[i]. This approach achieves O(n) time complexity and O(n) space complexity.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^2) | O(n) | Count intersection for each prefix directly |
| Optimal | O(n) | O(n) | Track number occurrences and increment count when seen in both arrays |
Algorithm Walkthrough
- Initialize an array
Cof lengthnto store the prefix common counts, all values initially zero. - Initialize a frequency array or dictionary
count_mapto track the number of times each number has appeared in either array. - Initialize a variable
common_countto 0. This keeps track of numbers that have appeared in both arrays up to the current index. - Iterate over indices
ifrom0ton - 1. At each step:
a. Increment the count for A[i] in count_map. If its count becomes 2, increment common_count.
b. Increment the count for B[i] in count_map. If its count becomes 2, increment common_count.
c. Assign C[i] = common_count.
5. Return the array C.
Why it works: By tracking how many times each number has appeared and updating the count only when a number has appeared in both arrays, the algorithm maintains an accurate running total of common elements for each prefix. The frequency array acts as a persistent record, eliminating repeated scans of prefixes.
Python Solution
from typing import List
from collections import defaultdict
class Solution:
def findThePrefixCommonArray(self, A: List[int], B: List[int]) -> List[int]:
n = len(A)
C = [0] * n
count_map = defaultdict(int)
common_count = 0
for i in range(n):
count_map[A[i]] += 1
if count_map[A[i]] == 2:
common_count += 1
count_map[B[i]] += 1
if count_map[B[i]] == 2:
common_count += 1
C[i] = common_count
return C
The Python implementation uses a defaultdict to automatically initialize counts to zero. We iterate once over the arrays, updating counts for A[i] and B[i]. Whenever a number’s count reaches 2, it has appeared in both arrays, and we update common_count. This running total is stored in C[i].
Go Solution
func findThePrefixCommonArray(A []int, B []int) []int {
n := len(A)
C := make([]int, n)
countMap := make([]int, n+1)
commonCount := 0
for i := 0; i < n; i++ {
countMap[A[i]]++
if countMap[A[i]] == 2 {
commonCount++
}
countMap[B[i]]++
if countMap[B[i]] == 2 {
commonCount++
}
C[i] = commonCount
}
return C
}
The Go version uses a slice countMap of length n+1 because numbers range from 1 to n, avoiding the need for a hash map. The logic mirrors the Python implementation. No nil checks are necessary since slices are initialized to zero values.
Worked Examples
Example 1: A = [1,3,2,4], B = [3,1,2,4]
| i | A[i] | B[i] | Seen in both | C[i] |
|---|---|---|---|---|
| 0 | 1 | 3 | {} | 0 |
| 1 | 3 | 1 | {1,3} | 2 |
| 2 | 2 | 2 | {1,2,3} | 3 |
| 3 | 4 | 4 | {1,2,3,4} | 4 |
Example 2: A = [2,3,1], B = [3,1,2]
| i | A[i] | B[i] | Seen in both | C[i] |
|---|---|---|---|---|
| 0 | 2 | 3 | {} | 0 |
| 1 | 3 | 1 | {3} | 1 |
| 2 | 1 | 2 | {1,2,3} | 3 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | We iterate through arrays once, updating counts in O(1) per element |
| Space | O(n) | We maintain a count map of size n+1 to track occurrences |
The algorithm scales linearly with the length of the arrays, and extra space is proportional to n.
Test Cases
# Provided examples
assert Solution().findThePrefixCommonArray([1,3,2,4], [3,1,2,4]) == [0,2,3,4] # Example 1
assert Solution().findThePrefixCommonArray([2,3,1], [3,1,2]) == [0,1,3] # Example 2
# Edge cases
assert Solution().findThePrefixCommonArray([1], [1]) == [1] # Single element
assert Solution().findThePrefixCommonArray([1,2,3], [1,2,3]) == [1,2,3] # Perfect match
assert Solution().findThePrefixCommonArray([3,2,1], [1,2,3]) == [0,1,3] # Reverse order
assert Solution().findThePrefixCommonArray([1,2,3,4,5], [5,4,3,2,1]) == [0,0,1,3,5] # Partial overlaps
| Test | Why |
|---|---|
| [1,3,2,4], [3,1,2,4] | Verifies multiple common elements appear gradually |
| [2,3,1], [3,1,2] | Checks common elements appear out of order |
| [1], [1] | Single-element edge case |
| [1,2,3], [1,2,3] | Arrays match exactly |
| [3,2,1], [1,2,3] | Reverse ordering |
| [1,2,3,4,5], [5,4,3,2,1] | Partial overlaps and scattered matches |
Edge Cases
Single-element arrays: If A and B have length 1, the only element may or may not match. The algorithm correctly counts a match when the only element appears in both arrays.
Reverse permutations: When A and B are in reverse order, common elements appear later. A naive prefix intersection might miscount if it assumes ordering, but the count-based approach correctly tracks elements independently of order.
No common elements in early prefixes: Early prefixes might have no common numbers. The algorithm initializes common_count to zero and only increments when a number is seen in both arrays, ensuring accurate counts from the first index onward.