2670. Find the Distinct Difference Array
Problem Description
You are given a 0-indexed array nums
of length n
.
For each position i
in the array, you need to calculate the distinct difference, which is:
- The number of distinct elements in the prefix
nums[0, ..., i]
(from start to positioni
, inclusive) - Minus the number of distinct elements in the suffix
nums[i + 1, ..., n - 1]
(from positioni + 1
to the end)
The result should be an array diff
where diff[i]
contains the distinct difference for position i
.
For example, if at position i
:
- The prefix
nums[0, ..., i]
has 5 distinct elements - The suffix
nums[i + 1, ..., n - 1]
has 3 distinct elements - Then
diff[i] = 5 - 3 = 2
Special case: When i = n - 1
(last element), the suffix is empty, so it has 0 distinct elements.
The problem asks you to return the distinct difference array for the entire input array nums
.
Intuition
The straightforward approach would be to calculate the distinct elements in the prefix and suffix for each position i
separately. However, this would involve repeatedly counting distinct elements, leading to inefficiency.
The key insight is that we can precompute the distinct element counts for all suffixes first. Why suffixes? Because as we iterate through the array from left to right to build our answer, we're naturally building prefixes incrementally - we can maintain the prefix's distinct elements using a set that grows as we move forward.
Here's the thought process:
-
Precompute suffix information: If we traverse the array from right to left, we can calculate and store the number of distinct elements for each suffix
nums[i, ..., n-1]
in an arraysuf
. We use a set to track distinct elements as we move leftward. -
Calculate the answer while building prefixes: Now traverse the array from left to right. At each position
i
:- We add
nums[i]
to our set, which now contains all distinct elements in the prefixnums[0, ..., i]
- The number of distinct elements in the prefix is simply the size of our set
- The number of distinct elements in the suffix
nums[i+1, ..., n-1]
is already stored insuf[i+1]
- The distinct difference is
len(prefix_set) - suf[i+1]
- We add
This approach is efficient because we only traverse the array twice and use sets for O(1) average-case lookups and insertions. We avoid recalculating the same information multiple times by preprocessing the suffix counts.
Solution Approach
The solution uses a hash table (set) and preprocessed suffix array to efficiently calculate the distinct differences.
Step 1: Preprocess Suffix Array
First, we create a suffix array suf
of size n + 1
, where suf[i]
stores the number of distinct elements in the suffix starting from index i
.
suf = [0] * (n + 1)
s = set()
for i in range(n - 1, -1, -1):
s.add(nums[i])
suf[i] = len(s)
- We traverse the array from right to left (from index
n-1
to0
) - For each position
i
, we addnums[i]
to the sets
- The size of the set gives us the number of distinct elements in
nums[i, ..., n-1]
- We store this count in
suf[i]
- Note:
suf[n] = 0
by default, representing an empty suffix
Step 2: Calculate Distinct Differences
Next, we clear the set and traverse the array from left to right to build the answer:
s.clear()
ans = [0] * n
for i, x in enumerate(nums):
s.add(x)
ans[i] = len(s) - suf[i + 1]
- We reuse the same set
s
, but now to track distinct elements in the prefix - For each position
i
:- Add
nums[i]
to the set (building the prefixnums[0, ..., i]
) len(s)
gives the number of distinct elements in the prefixsuf[i + 1]
gives the number of distinct elements in the suffixnums[i + 1, ..., n - 1]
- The distinct difference is
len(s) - suf[i + 1]
- Add
Time Complexity: O(n)
- We traverse the array twice, and set operations (add, len) are O(1) on average.
Space Complexity: O(n)
- We use an additional array suf
of size n + 1
and a set that can contain at most n
elements.
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 the solution with nums = [3, 2, 3, 4, 2]
.
Step 1: Build the suffix array from right to left
We create suf = [0, 0, 0, 0, 0, 0]
and an empty set s
.
- i = 4: Add
nums[4] = 2
to set →s = {2}
→suf[4] = 1
- i = 3: Add
nums[3] = 4
to set →s = {2, 4}
→suf[3] = 2
- i = 2: Add
nums[2] = 3
to set →s = {2, 3, 4}
→suf[2] = 3
- i = 1: Add
nums[1] = 2
to set →s = {2, 3, 4}
(2 already exists) →suf[1] = 3
- i = 0: Add
nums[0] = 3
to set →s = {2, 3, 4}
(3 already exists) →suf[0] = 3
Result: suf = [3, 3, 3, 2, 1, 0]
This means:
suf[0] = 3
: suffix starting at index 0 has 3 distinct elementssuf[1] = 3
: suffix starting at index 1 has 3 distinct elementssuf[2] = 3
: suffix starting at index 2 has 3 distinct elementssuf[3] = 2
: suffix starting at index 3 has 2 distinct elementssuf[4] = 1
: suffix starting at index 4 has 1 distinct elementsuf[5] = 0
: empty suffix has 0 distinct elements
Step 2: Calculate distinct differences from left to right
Clear the set and create ans = [0, 0, 0, 0, 0]
.
- i = 0: Add
nums[0] = 3
to set →s = {3}
→ prefix has 1 distinct, suffixsuf[1] = 3
→ans[0] = 1 - 3 = -2
- i = 1: Add
nums[1] = 2
to set →s = {2, 3}
→ prefix has 2 distinct, suffixsuf[2] = 3
→ans[1] = 2 - 3 = -1
- i = 2: Add
nums[2] = 3
to set →s = {2, 3}
(3 already exists) → prefix has 2 distinct, suffixsuf[3] = 2
→ans[2] = 2 - 2 = 0
- i = 3: Add
nums[3] = 4
to set →s = {2, 3, 4}
→ prefix has 3 distinct, suffixsuf[4] = 1
→ans[3] = 3 - 1 = 2
- i = 4: Add
nums[4] = 2
to set →s = {2, 3, 4}
(2 already exists) → prefix has 3 distinct, suffixsuf[5] = 0
→ans[4] = 3 - 0 = 3
Final answer: [-2, -1, 0, 2, 3]
This matches our expected result where:
- At index 0: prefix
[3]
has 1 distinct, suffix[2,3,4,2]
has 3 distinct → 1 - 3 = -2 - At index 1: prefix
[3,2]
has 2 distinct, suffix[3,4,2]
has 3 distinct → 2 - 3 = -1 - At index 2: prefix
[3,2,3]
has 2 distinct, suffix[4,2]
has 2 distinct → 2 - 2 = 0 - At index 3: prefix
[3,2,3,4]
has 3 distinct, suffix[2]
has 1 distinct → 3 - 1 = 2 - At index 4: prefix
[3,2,3,4,2]
has 3 distinct, suffix[]
has 0 distinct → 3 - 0 = 3
Solution Implementation
1class Solution:
2 def distinctDifferenceArray(self, nums: List[int]) -> List[int]:
3 """
4 Calculate the distinct difference array where each element represents
5 the difference between distinct elements in prefix and suffix.
6
7 For each index i: result[i] = distinct_count(nums[0:i+1]) - distinct_count(nums[i+1:n])
8
9 Args:
10 nums: Input list of integers
11
12 Returns:
13 List where each element is the difference between distinct elements
14 in prefix and suffix at that position
15 """
16 n = len(nums)
17
18 # suffix_distinct_count[i] stores the count of distinct elements from index i to end
19 suffix_distinct_count = [0] * (n + 1)
20
21 # Build suffix distinct counts by traversing from right to left
22 distinct_elements = set()
23 for i in range(n - 1, -1, -1):
24 distinct_elements.add(nums[i])
25 suffix_distinct_count[i] = len(distinct_elements)
26
27 # Clear the set for reuse in prefix calculation
28 distinct_elements.clear()
29
30 # Initialize result array
31 result = [0] * n
32
33 # Calculate prefix distinct counts and compute the difference
34 for i, num in enumerate(nums):
35 distinct_elements.add(num)
36 # prefix_distinct_count - suffix_distinct_count (excluding current element)
37 result[i] = len(distinct_elements) - suffix_distinct_count[i + 1]
38
39 return result
40
1class Solution {
2 public int[] distinctDifferenceArray(int[] nums) {
3 int n = nums.length;
4
5 // Array to store count of distinct elements from each position to the end
6 // suffixDistinctCount[i] represents the number of distinct elements from index i to end
7 int[] suffixDistinctCount = new int[n + 1];
8
9 // Set to track unique elements while traversing
10 Set<Integer> uniqueElements = new HashSet<>();
11
12 // Build suffix distinct count array by traversing from right to left
13 for (int i = n - 1; i >= 0; i--) {
14 uniqueElements.add(nums[i]);
15 suffixDistinctCount[i] = uniqueElements.size();
16 }
17
18 // Clear the set for reuse in prefix calculation
19 uniqueElements.clear();
20
21 // Result array to store the difference values
22 int[] result = new int[n];
23
24 // Calculate prefix distinct count and compute the difference
25 for (int i = 0; i < n; i++) {
26 // Add current element to prefix set
27 uniqueElements.add(nums[i]);
28
29 // Calculate difference: distinct elements in prefix[0..i] minus distinct elements in suffix[i+1..n-1]
30 result[i] = uniqueElements.size() - suffixDistinctCount[i + 1];
31 }
32
33 return result;
34 }
35}
36
1class Solution {
2public:
3 vector<int> distinctDifferenceArray(vector<int>& nums) {
4 int n = nums.size();
5
6 // Array to store count of distinct elements from index i to end
7 // suffixCount[i] = number of distinct elements in nums[i..n-1]
8 vector<int> suffixCount(n + 1, 0);
9
10 // Set to track unique elements while building suffix counts
11 unordered_set<int> uniqueElements;
12
13 // Build suffix distinct count array from right to left
14 for (int i = n - 1; i >= 0; --i) {
15 uniqueElements.insert(nums[i]);
16 suffixCount[i] = uniqueElements.size();
17 }
18
19 // Clear set for reuse in prefix calculation
20 uniqueElements.clear();
21
22 // Result array to store the difference values
23 vector<int> result(n);
24
25 // Calculate prefix distinct count and compute difference
26 for (int i = 0; i < n; ++i) {
27 // Add current element to prefix set
28 uniqueElements.insert(nums[i]);
29
30 // Calculate difference: prefix[0..i] - suffix[i+1..n-1]
31 // prefixCount = uniqueElements.size()
32 // suffixCount[i+1] = distinct elements from i+1 to end
33 result[i] = uniqueElements.size() - suffixCount[i + 1];
34 }
35
36 return result;
37 }
38};
39
1/**
2 * Calculates the distinct difference array where each element represents
3 * the difference between distinct elements in prefix and suffix
4 * @param nums - Input array of numbers
5 * @returns Array where ans[i] = distinct(nums[0..i]) - distinct(nums[i+1..n-1])
6 */
7function distinctDifferenceArray(nums: number[]): number[] {
8 const arrayLength: number = nums.length;
9
10 // Array to store count of distinct elements in suffix starting from each index
11 // suffixDistinctCount[i] represents distinct elements from index i to end
12 const suffixDistinctCount: number[] = Array(arrayLength + 1).fill(0);
13
14 // Set to track unique elements
15 const uniqueElements: Set<number> = new Set<number>();
16
17 // Build suffix distinct count array by traversing from right to left
18 for (let i: number = arrayLength - 1; i >= 0; i--) {
19 uniqueElements.add(nums[i]);
20 suffixDistinctCount[i] = uniqueElements.size;
21 }
22
23 // Clear the set for reuse in prefix calculation
24 uniqueElements.clear();
25
26 // Result array to store the distinct differences
27 const result: number[] = Array(arrayLength).fill(0);
28
29 // Calculate distinct difference for each position
30 // by building prefix distinct count and subtracting suffix distinct count
31 for (let i: number = 0; i < arrayLength; i++) {
32 uniqueElements.add(nums[i]);
33 // Distinct elements in prefix[0..i] minus distinct elements in suffix[i+1..n-1]
34 result[i] = uniqueElements.size - suffixDistinctCount[i + 1];
35 }
36
37 return result;
38}
39
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm consists of two sequential passes through the array:
- The first loop iterates from index
n-1
to0
, performing constant-time operations (set addition and length calculation) at each step, takingO(n)
time. - The second loop iterates through all elements using enumerate, performing constant-time operations (set addition, length calculation, and subtraction) at each step, also taking
O(n)
time. - Since these loops run sequentially, the total time complexity is
O(n) + O(n) = O(n)
.
The space complexity is O(n)
, where n
is the length of the array nums
. The space usage includes:
- The
suf
array of sizen + 1
, which requiresO(n)
space. - The
ans
array of sizen
, which requiresO(n)
space. - The set
s
which can contain at mostn
distinct elements fromnums
, requiringO(n)
space in the worst case. - The total space complexity is
O(n) + O(n) + O(n) = O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Error in Suffix Array Indexing
The Pitfall:
A common mistake is incorrectly indexing the suffix array when calculating the distinct difference. Developers often confuse whether suf[i]
should represent:
- The suffix starting from index
i
(includingnums[i]
), or - The suffix starting from index
i + 1
(excludingnums[i]
)
This confusion leads to using suf[i]
instead of suf[i + 1]
when calculating the answer, resulting in incorrect results.
Example of Incorrect Code:
# WRONG: Using suf[i] instead of suf[i + 1]
for i, x in enumerate(nums):
s.add(x)
ans[i] = len(s) - suf[i] # ❌ This includes nums[i] in both prefix and suffix
Why This Happens:
The problem requires the suffix to start from i + 1
(not including the current element), but when building the suffix array, suf[i]
naturally stores the count starting from index i
. This creates a mismatch between the stored values and what we need.
The Solution:
Always use suf[i + 1]
when calculating the distinct difference at position i
:
for i, x in enumerate(nums):
s.add(x)
ans[i] = len(s) - suf[i + 1] # ✅ Correct: suffix starts from i + 1
2. Not Clearing the Set Between Prefix and Suffix Calculations
The Pitfall: Forgetting to clear the set after building the suffix array and before calculating the prefix leads to incorrect prefix counts.
Example of Incorrect Code:
# Build suffix array
s = set()
for i in range(n - 1, -1, -1):
s.add(nums[i])
suf[i] = len(s)
# WRONG: Forgot to clear the set
# s.clear() # ❌ Missing this line
ans = [0] * n
for i, x in enumerate(nums):
s.add(x) # Set already contains all elements from suffix calculation
ans[i] = len(s) - suf[i + 1]
The Solution: Always clear the set between the two phases:
s.clear() # ✅ Essential: Reset the set before prefix calculation
3. Incorrect Suffix Array Size
The Pitfall:
Creating the suffix array with size n
instead of n + 1
, which causes an index out of bounds error when accessing suf[n]
(needed when i = n - 1
).
Example of Incorrect Code:
suf = [0] * n # ❌ Should be n + 1
# Later when i = n - 1:
ans[n-1] = len(s) - suf[n] # IndexError: list index out of range
The Solution:
Always allocate n + 1
elements for the suffix array:
suf = [0] * (n + 1) # ✅ Correct size to handle suf[n] = 0
The extra element suf[n]
represents an empty suffix with 0 distinct elements.
Which type of traversal does breadth first search do?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!