2913. Subarrays Distinct Element Sum of Squares I
Problem Description
You have an integer array nums
that is 0-indexed.
For any subarray of nums
, you need to find its distinct count, which is the number of unique/distinct values present in that subarray. A subarray nums[i..j]
includes all elements from index i
to index j
(inclusive), where 0 <= i <= j < nums.length
.
Your task is to:
- Find all possible subarrays of
nums
- Calculate the distinct count for each subarray
- Square each distinct count
- Return the sum of all these squared values
For example, if a subarray has 3 distinct values, its contribution to the final answer would be 3 * 3 = 9
.
Key Points:
- A subarray must be contiguous (elements next to each other in the original array)
- A subarray cannot be empty
- You need to consider all possible subarrays
- The distinct count is the number of unique elements in a subarray (duplicates count as one)
The solution approach iterates through all possible starting positions i
, and for each starting position, extends the subarray by incrementing the ending position j
. As each new element nums[j]
is added to the current subarray, it's added to a set s
which automatically handles uniqueness. The size of this set represents the distinct count, and len(s) * len(s)
gives the square of the distinct count, which is added to the running total.
Intuition
To solve this problem, we need to examine every possible subarray and calculate its distinct count. The most straightforward way to think about this is to consider how we can systematically generate all subarrays.
A subarray is defined by two things: where it starts and where it ends. If we fix a starting position i
, we can extend the subarray one element at a time by moving the ending position j
from i
to the end of the array. This gives us all subarrays that start at position i
.
The key insight is that when we extend a subarray by one element (moving from nums[i..j]
to nums[i..j+1]
), we're just adding one new element nums[j+1]
to our existing subarray. This means we don't need to recalculate the distinct count from scratch each time - we can maintain it incrementally.
A set is the perfect data structure for tracking distinct elements because:
- It automatically handles duplicates (adding the same element twice doesn't change the set)
- We can query its size in constant time to get the distinct count
- Adding an element is efficient
So the approach becomes:
- For each starting position
i
, create an empty set - Extend the subarray one element at a time (incrementing
j
) - Add each new element to the set
- The size of the set at any point gives us the distinct count for the current subarray
- Square this count and add it to our answer
This way, we process each subarray exactly once, and for each subarray, we efficiently calculate its contribution (distinct_count²
) to the final sum.
Solution Approach
The solution uses a nested loop approach with a set data structure to efficiently track distinct elements.
Algorithm Steps:
-
Initialize variables: Set
ans = 0
to store the final sum and get the length of the arrayn = len(nums)
. -
Outer loop - Fix the starting position: Iterate through each possible starting index
i
from0
ton-1
. -
Initialize a set for each starting position: For each
i
, create an empty sets = set()
. This will track the distinct elements in subarrays starting at indexi
. -
Inner loop - Extend the subarray: For each starting position
i
, iteratej
fromi
ton-1
. This represents extending the subarray one element at a time. -
Update the set and calculate contribution:
- Add the current element
nums[j]
to the set:s.add(nums[j])
- The set automatically handles duplicates, so
len(s)
gives us the current distinct count - Calculate the square of the distinct count:
len(s) * len(s)
- Add this squared value to our answer:
ans += len(s) * len(s)
- Add the current element
-
Return the result: After processing all subarrays, return
ans
.
Why this works:
- When
j = i
, we have a subarray with just one elementnums[i]
- As
j
increases, we're considering subarraysnums[i..i+1]
,nums[i..i+2]
, and so on - The set maintains the distinct elements seen so far in the current subarray
- By adding
len(s) * len(s)
at each step, we're accounting for each subarray exactly once
Time Complexity: O(n²)
where n
is the length of the array, as we have two nested loops.
Space Complexity: O(n)
in the worst case when all elements in a subarray are distinct.
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 = [1, 2, 1]
.
We need to find all subarrays, calculate their distinct counts, square them, and sum the results.
Step-by-step execution:
When i = 0 (starting at index 0):
- Initialize empty set
s = {}
- j = 0: Add nums[0] = 1 to set → s = {1}
- Subarray [1] has distinct count = 1
- Contribution: 1² = 1
- Running total: ans = 1
- j = 1: Add nums[1] = 2 to set → s = {1, 2}
- Subarray [1, 2] has distinct count = 2
- Contribution: 2² = 4
- Running total: ans = 1 + 4 = 5
- j = 2: Add nums[2] = 1 to set → s = {1, 2} (1 already exists)
- Subarray [1, 2, 1] has distinct count = 2
- Contribution: 2² = 4
- Running total: ans = 5 + 4 = 9
When i = 1 (starting at index 1):
- Initialize empty set
s = {}
- j = 1: Add nums[1] = 2 to set → s = {2}
- Subarray [2] has distinct count = 1
- Contribution: 1² = 1
- Running total: ans = 9 + 1 = 10
- j = 2: Add nums[2] = 1 to set → s = {2, 1}
- Subarray [2, 1] has distinct count = 2
- Contribution: 2² = 4
- Running total: ans = 10 + 4 = 14
When i = 2 (starting at index 2):
- Initialize empty set
s = {}
- j = 2: Add nums[2] = 1 to set → s = {1}
- Subarray [1] has distinct count = 1
- Contribution: 1² = 1
- Running total: ans = 14 + 1 = 15
Final answer: 15
This demonstrates how the set efficiently tracks distinct elements as we extend each subarray, and how we calculate the squared distinct count for each subarray's contribution to the final sum.
Solution Implementation
1class Solution:
2 def sumCounts(self, nums: List[int]) -> int:
3 """
4 Calculate the sum of squares of distinct element counts for all subarrays.
5
6 For each subarray, count the number of distinct elements and square it.
7 Return the sum of all these squared counts.
8
9 Args:
10 nums: List of integers
11
12 Returns:
13 Sum of squared distinct counts for all subarrays
14 """
15 total_sum = 0
16 array_length = len(nums)
17
18 # Iterate through all possible starting positions of subarrays
19 for start_index in range(array_length):
20 # Set to track distinct elements in current subarray
21 distinct_elements = set()
22
23 # Extend the subarray from start_index to each possible ending position
24 for end_index in range(start_index, array_length):
25 # Add current element to the set of distinct elements
26 distinct_elements.add(nums[end_index])
27
28 # Calculate square of distinct count and add to total
29 distinct_count = len(distinct_elements)
30 total_sum += distinct_count * distinct_count
31
32 return total_sum
33
1class Solution {
2 public int sumCounts(List<Integer> nums) {
3 int totalSum = 0;
4 int n = nums.size();
5
6 // Iterate through all possible starting positions of subarrays
7 for (int startIndex = 0; startIndex < n; ++startIndex) {
8 // Frequency array to track occurrences of each number (0-100 range)
9 int[] frequency = new int[101];
10 // Count of distinct elements in current subarray
11 int distinctCount = 0;
12
13 // Extend the subarray from startIndex to each possible ending position
14 for (int endIndex = startIndex; endIndex < n; ++endIndex) {
15 // Get the current element value
16 int currentElement = nums.get(endIndex);
17
18 // Increment frequency and check if it's a new distinct element
19 frequency[currentElement]++;
20 if (frequency[currentElement] == 1) {
21 // First occurrence of this element in current subarray
22 distinctCount++;
23 }
24
25 // Add the square of distinct count to the total sum
26 // For subarray from startIndex to endIndex
27 totalSum += distinctCount * distinctCount;
28 }
29 }
30
31 return totalSum;
32 }
33}
34
1class Solution {
2public:
3 int sumCounts(vector<int>& nums) {
4 int totalSum = 0;
5 int n = nums.size();
6
7 // Iterate through all possible starting positions of subarrays
8 for (int i = 0; i < n; ++i) {
9 // Frequency array to track occurrences of each element (assumes values are in range [0, 100])
10 int frequency[101] = {0};
11 int distinctCount = 0;
12
13 // Extend the subarray from position i to the end
14 for (int j = i; j < n; ++j) {
15 // Increment frequency of current element
16 // If this is the first occurrence (frequency becomes 1), increment distinct count
17 if (++frequency[nums[j]] == 1) {
18 ++distinctCount;
19 }
20
21 // Add the square of distinct count to the total sum
22 totalSum += distinctCount * distinctCount;
23 }
24 }
25
26 return totalSum;
27 }
28};
29
1/**
2 * Calculates the sum of squares of distinct element counts for all subarrays
3 * @param nums - Input array of numbers
4 * @returns Sum of (distinct count)^2 for all subarrays
5 */
6function sumCounts(nums: number[]): number {
7 let totalSum: number = 0;
8 const arrayLength: number = nums.length;
9
10 // Iterate through each starting position of subarrays
11 for (let startIndex: number = 0; startIndex < arrayLength; startIndex++) {
12 // Frequency array to track occurrences of each number (0-100 range)
13 const frequencyMap: number[] = Array(101).fill(0);
14 let distinctCount: number = 0;
15
16 // Process all subarrays starting from startIndex
17 for (let endIndex: number = startIndex; endIndex < arrayLength; endIndex++) {
18 const currentElement: number = nums[endIndex];
19
20 // Increment frequency and check if it's a new distinct element
21 frequencyMap[currentElement]++;
22 if (frequencyMap[currentElement] === 1) {
23 distinctCount++;
24 }
25
26 // Add square of current distinct count to total sum
27 totalSum += distinctCount * distinctCount;
28 }
29 }
30
31 return totalSum;
32}
33
Time and Space Complexity
The time complexity is O(n^2)
, where n
is the length of the array nums
. This is because we have two nested loops: the outer loop runs n
times (iterating through each starting position i
), and for each iteration of the outer loop, the inner loop runs from position i
to n-1
, which on average contributes to n/2
iterations. The total number of operations is approximately n * n/2
, which simplifies to O(n^2)
.
The space complexity is O(n)
. This is determined by the set s
which, in the worst case, can store all unique elements from a subarray. Since the maximum number of unique elements in any subarray cannot exceed the total number of elements in the array, the set can grow up to size n
. The other variables (ans
, n
, i
, j
) use constant space O(1)
, so the overall space complexity is dominated by the set, resulting in O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Confusing Subarrays with Subsequences
A frequent mistake is treating this problem as finding all possible subsequences instead of subarrays. Remember that subarrays must be contiguous (consecutive elements), while subsequences can skip elements.
Incorrect approach:
# Wrong: This would generate subsequences, not subarrays
def wrong_approach(nums):
# Trying to use combinations or bit manipulation to get all subsets
from itertools import combinations
total = 0
for length in range(1, len(nums) + 1):
for subset in combinations(nums, length):
distinct = len(set(subset))
total += distinct * distinct
return total
Correct approach: Use nested loops where the inner loop extends from the current starting position, ensuring contiguity.
2. Recreating the Set for Each Subarray
Some might think they need to recreate the set from scratch for every single subarray, leading to inefficient code:
Inefficient approach:
def inefficient_approach(nums):
total_sum = 0
n = len(nums)
for i in range(n):
for j in range(i, n):
# Creating a new set for each subarray nums[i:j+1]
distinct_elements = set(nums[i:j+1]) # O(j-i+1) operation
total_sum += len(distinct_elements) ** 2
return total_sum
This approach has O(n³) time complexity because slicing and creating a set for each subarray takes O(n) time in the worst case.
Optimized approach: Build the set incrementally as shown in the original solution, which maintains O(n²) complexity.
3. Integer Overflow in Other Languages
While Python handles large integers automatically, in languages like Java or C++, squaring the distinct count and summing could cause integer overflow.
Solution for other languages:
- Use
long
orlong long
data types - Consider using modulo arithmetic if the problem specifies it
// Java example long totalSum = 0; // ... totalSum += (long) distinctCount * distinctCount;
4. Off-by-One Errors in Loop Boundaries
It's easy to make mistakes with the loop boundaries, especially forgetting that the end index should be inclusive:
Common mistake:
# Wrong: Missing the last element in some subarrays
for end_index in range(start_index, array_length - 1): # Should be array_length
distinct_elements.add(nums[end_index])
Correct: Ensure the inner loop goes up to array_length - 1
(inclusive) by using range(start_index, array_length)
.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
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!