1399. Count Largest Group
Problem Description
You need to group all integers from 1
to n
based on their digit sum. Two numbers belong to the same group if the sum of their digits is equal.
For example:
-
The number
14
has digit sum1 + 4 = 5
-
The number
5
has digit sum5
-
So
14
and5
belong to the same group (both have digit sum of5
) -
The number
13
has digit sum1 + 3 = 4
-
The number
3
has digit sum3
-
So
13
and3
belong to different groups (digit sums of4
and3
respectively)
After grouping all numbers from 1
to n
by their digit sums, you need to find the size of the largest group(s). Then return how many groups have this maximum size.
For instance, if after grouping you have:
- Group with digit sum
3
: contains2
numbers - Group with digit sum
4
: contains3
numbers - Group with digit sum
5
: contains3
numbers - Group with digit sum
6
: contains1
number
The maximum group size is 3
, and there are 2
groups with this size (digit sums 4
and 5
), so the answer would be 2
.
Intuition
The key insight is that we need to track two things: the size of each group and which group size is the largest.
Since we're grouping numbers by their digit sum, we can use the digit sum as a key to count how many numbers fall into each group. For any number from 1
to n
, we can calculate its digit sum by repeatedly taking the remainder when divided by 10
(to get the last digit) and then dividing by 10
(to remove the last digit).
As we process each number from 1
to n
, we calculate its digit sum and increment the count for that particular sum. This naturally builds up our groups.
The clever part is tracking the answer as we go. Instead of first building all groups and then finding the maximum size and counting how many groups have that size, we can do it in one pass:
- Whenever we encounter a group that becomes larger than our current maximum size, we know this is now the only group with the maximum size, so we reset our answer to
1
- Whenever we encounter a group that equals our current maximum size, we've found another group with the maximum size, so we increment our answer
This way, by the time we've processed all numbers from 1
to n
, we already have our answer without needing a second pass through the data.
The constraint that n ≤ 10^4
tells us the maximum possible digit sum is 9 + 9 + 9 + 9 = 36
(for 9999
), so we know our groups are limited to at most 36
different digit sums, making this approach very efficient.
Learn more about Math patterns.
Solution Approach
We implement the solution using a hash table (or array) to count occurrences of each digit sum.
Data Structure Setup:
- Create a counter
cnt
to track how many numbers have each digit sum - Initialize variables
mx = 0
to track the maximum group size andans = 0
to count groups with maximum size
Main Algorithm:
For each number i
from 1
to n
:
-
Calculate digit sum:
- Initialize
s = 0
to store the sum - While
i > 0
:- Add the last digit to sum:
s += i % 10
- Remove the last digit:
i //= 10
- Add the last digit to sum:
- Initialize
-
Update the count for this digit sum:
- Increment
cnt[s]
by1
- Increment
-
Update the answer based on the new count:
- If
cnt[s] > mx
: We found a new maximum group size- Update
mx = cnt[s]
- Reset
ans = 1
(only one group has this new maximum size)
- Update
- Else if
cnt[s] == mx
: We found another group with the maximum size- Increment
ans += 1
- Increment
- If
Why this works:
- Since we know the maximum digit sum is
36
(from9999
), we could alternatively use an array of size40
instead of a hash table - By tracking
mx
andans
during the iteration, we avoid a second pass through the data - The counter naturally groups numbers by their digit sum as we process them
Time Complexity: O(n × log n)
where the log n
factor comes from calculating the digit sum of each number (number of digits is proportional to log n
)
Space Complexity: O(1)
since we're storing at most 36
different digit sums
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 n = 15
.
Initial Setup:
cnt = {}
(empty counter for digit sums)mx = 0
(maximum group size)ans = 0
(count of groups with maximum size)
Processing each number from 1 to 15:
i | Digit Sum Calculation | s | cnt[s] | mx | ans | Explanation |
---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | New max size, reset ans to 1 |
2 | 2 | 2 | 1 | 1 | 2 | Equals max, increment ans |
3 | 3 | 3 | 1 | 1 | 3 | Equals max, increment ans |
4 | 4 | 4 | 1 | 1 | 4 | Equals max, increment ans |
5 | 5 | 5 | 1 | 1 | 5 | Equals max, increment ans |
6 | 6 | 6 | 1 | 1 | 6 | Equals max, increment ans |
7 | 7 | 7 | 1 | 1 | 7 | Equals max, increment ans |
8 | 8 | 8 | 1 | 1 | 8 | Equals max, increment ans |
9 | 9 | 9 | 1 | 1 | 9 | Equals max, increment ans |
10 | 1+0 = 1 | 1 | 2 | 2 | 1 | New max size, reset ans to 1 |
11 | 1+1 = 2 | 2 | 2 | 2 | 2 | Equals max, increment ans |
12 | 1+2 = 3 | 3 | 2 | 2 | 3 | Equals max, increment ans |
13 | 1+3 = 4 | 4 | 2 | 2 | 4 | Equals max, increment ans |
14 | 1+4 = 5 | 5 | 2 | 2 | 5 | Equals max, increment ans |
15 | 1+5 = 6 | 6 | 2 | 2 | 6 | Equals max, increment ans |
Final Groups:
- Digit sum 1: {1, 10} → size 2
- Digit sum 2: {2, 11} → size 2
- Digit sum 3: {3, 12} → size 2
- Digit sum 4: {4, 13} → size 2
- Digit sum 5: {5, 14} → size 2
- Digit sum 6: {6, 15} → size 2
- Digit sum 7: {7} → size 1
- Digit sum 8: {8} → size 1
- Digit sum 9: {9} → size 1
Result: Maximum group size is 2, and there are 6 groups with this size, so the answer is 6.
The key insight demonstrated here is how we dynamically track the answer:
- When we hit number 10, it creates the first group of size 2 (digit sum 1), making it the new maximum
- As we continue, numbers 11-15 each create groups of size 2, incrementing our answer each time
- We never need to look back at our groups - the answer is computed on the fly
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def countLargestGroup(self, n: int) -> int:
5 # Dictionary to count frequency of each digit sum
6 digit_sum_count = Counter()
7 # Track the maximum group size and count of groups with that size
8 num_largest_groups = 0
9 max_group_size = 0
10
11 # Iterate through all numbers from 1 to n
12 for number in range(1, n + 1):
13 # Calculate sum of digits for current number
14 digit_sum = 0
15 temp = number
16 while temp > 0:
17 digit_sum += temp % 10 # Add the last digit
18 temp //= 10 # Remove the last digit
19
20 # Increment count for this digit sum
21 digit_sum_count[digit_sum] += 1
22
23 # Update tracking variables
24 if digit_sum_count[digit_sum] > max_group_size:
25 # Found a new maximum group size
26 max_group_size = digit_sum_count[digit_sum]
27 num_largest_groups = 1
28 elif digit_sum_count[digit_sum] == max_group_size:
29 # Found another group with the same maximum size
30 num_largest_groups += 1
31
32 return num_largest_groups
33
1class Solution {
2 public int countLargestGroup(int n) {
3 // Array to store count of numbers for each possible digit sum
4 // Maximum digit sum for n <= 10^4 is 9+9+9+9 = 36, so size 40 is sufficient
5 int[] digitSumCount = new int[40];
6
7 // Track the count of groups with maximum size
8 int largestGroupCount = 0;
9 // Track the maximum group size found so far
10 int maxGroupSize = 0;
11
12 // Iterate through all numbers from 1 to n
13 for (int currentNumber = 1; currentNumber <= n; ++currentNumber) {
14 // Calculate the sum of digits for current number
15 int digitSum = 0;
16 for (int temp = currentNumber; temp > 0; temp /= 10) {
17 digitSum += temp % 10; // Extract and add the last digit
18 }
19
20 // Increment the count for this digit sum
21 ++digitSumCount[digitSum];
22
23 // Check if we found a new maximum group size
24 if (maxGroupSize < digitSumCount[digitSum]) {
25 maxGroupSize = digitSumCount[digitSum];
26 largestGroupCount = 1; // Reset count to 1 for new maximum
27 } else if (maxGroupSize == digitSumCount[digitSum]) {
28 // Found another group with the same maximum size
29 ++largestGroupCount;
30 }
31 }
32
33 return largestGroupCount;
34 }
35}
36
1class Solution {
2public:
3 int countLargestGroup(int n) {
4 // Array to count the frequency of each digit sum
5 // Maximum possible sum is 9*4=36 for n=9999 (max n is 10^4)
6 int digitSumFrequency[40]{};
7
8 // Track the count of groups with maximum size
9 int largestGroupCount = 0;
10 // Track the maximum group size seen so far
11 int maxGroupSize = 0;
12
13 // Iterate through all numbers from 1 to n
14 for (int number = 1; number <= n; ++number) {
15 // Calculate the sum of digits for current number
16 int digitSum = 0;
17 for (int temp = number; temp > 0; temp /= 10) {
18 digitSum += temp % 10; // Extract and add the last digit
19 }
20
21 // Increment the frequency count for this digit sum
22 ++digitSumFrequency[digitSum];
23
24 // Check if we found a new maximum group size
25 if (maxGroupSize < digitSumFrequency[digitSum]) {
26 maxGroupSize = digitSumFrequency[digitSum];
27 largestGroupCount = 1; // Reset count to 1 for new maximum
28 }
29 // Check if current group size equals the maximum
30 else if (maxGroupSize == digitSumFrequency[digitSum]) {
31 ++largestGroupCount; // Increment count of largest groups
32 }
33 }
34
35 return largestGroupCount;
36 }
37};
38
1/**
2 * Counts the number of groups that have the maximum size when grouping numbers by their digit sum
3 * @param n - The upper bound of the range [1, n] to check
4 * @returns The count of groups with the maximum size
5 */
6function countLargestGroup(n: number): number {
7 // Array to store count of numbers for each possible digit sum
8 // Maximum possible sum is 9*4 = 36 (for 9999), so 40 is sufficient
9 const digitSumCounts: number[] = Array(40).fill(0);
10
11 // Track the maximum group size found
12 let maxGroupSize: number = 0;
13
14 // Count of groups that have the maximum size
15 let groupsWithMaxSize: number = 0;
16
17 // Iterate through all numbers from 1 to n
18 for (let currentNumber: number = 1; currentNumber <= n; ++currentNumber) {
19 // Calculate the sum of digits for the current number
20 let digitSum: number = 0;
21 let tempNumber: number = currentNumber;
22
23 // Extract and sum each digit
24 while (tempNumber > 0) {
25 digitSum += tempNumber % 10;
26 tempNumber = Math.floor(tempNumber / 10);
27 }
28
29 // Increment the count for this digit sum
30 ++digitSumCounts[digitSum];
31
32 // Check if we found a new maximum group size
33 if (maxGroupSize < digitSumCounts[digitSum]) {
34 maxGroupSize = digitSumCounts[digitSum];
35 groupsWithMaxSize = 1; // Reset count to 1 for the new maximum
36 } else if (maxGroupSize === digitSumCounts[digitSum]) {
37 // Found another group with the same maximum size
38 ++groupsWithMaxSize;
39 }
40 }
41
42 return groupsWithMaxSize;
43}
44
Time and Space Complexity
Time Complexity: O(n × log n)
The algorithm iterates through all numbers from 1 to n, which takes O(n)
iterations. For each number i
, we calculate the sum of its digits by repeatedly dividing by 10 and taking the remainder. The number of digits in a number i
is approximately log₁₀(i)
, so extracting all digits takes O(log i)
time. In the worst case when i = n
, this becomes O(log n)
. Therefore, the overall time complexity is O(n × log n)
.
Space Complexity: O(log n)
The space is primarily used by the Counter
dictionary cnt
. The key insight is that the dictionary stores digit sums as keys. The maximum possible digit sum occurs for the number n
, and since n
has at most O(log n)
digits, and each digit can be at most 9, the maximum possible sum is 9 × O(log n) = O(log n)
. The number of unique digit sums is bounded by this maximum value, so the dictionary can have at most O(log n)
entries. Therefore, the space complexity is O(log n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Loop Variable While Calculating Digit Sum
A common mistake is directly modifying the loop variable i
when calculating the digit sum:
Incorrect Code:
for i in range(1, n + 1):
digit_sum = 0
while i > 0: # BUG: Modifying loop variable i
digit_sum += i % 10
i //= 10 # This destroys the original value of i
# Now i is 0, not the original number!
digit_sum_count[digit_sum] += 1
Why it's wrong: After calculating the digit sum, i
becomes 0
, so you lose track of which number you were processing. This breaks the loop iteration.
Solution: Always use a temporary variable to preserve the original value:
for i in range(1, n + 1):
digit_sum = 0
temp = i # Create a copy
while temp > 0:
digit_sum += temp % 10
temp //= 10
# i is still intact
digit_sum_count[digit_sum] += 1
2. Incorrect Answer Tracking Logic
Another pitfall is resetting the counter at the wrong time or not handling the first occurrence correctly:
Incorrect Code:
if digit_sum_count[digit_sum] >= max_group_size: # BUG: Using >= instead of >
max_group_size = digit_sum_count[digit_sum]
num_largest_groups = 1 # This resets even when equal!
Why it's wrong: Using >=
would reset the counter every time we see a group with the current maximum size, losing count of previous groups with the same size.
Solution: Use strict comparison operators correctly:
if digit_sum_count[digit_sum] > max_group_size: # New maximum found max_group_size = digit_sum_count[digit_sum] num_largest_groups = 1 elif digit_sum_count[digit_sum] == max_group_size: # Another group with same maximum size num_largest_groups += 1
3. Post-Processing Instead of Real-Time Tracking
Some might try to find the answer after building the complete frequency map:
Less Efficient Approach:
# Build complete frequency map first
for i in range(1, n + 1):
digit_sum = calculate_digit_sum(i)
digit_sum_count[digit_sum] += 1
# Then find the answer (requires second pass)
max_size = max(digit_sum_count.values())
return sum(1 for count in digit_sum_count.values() if count == max_size)
Why it's suboptimal: This requires two passes through the data - one to build the map and another to find the answer.
Solution: Track the answer while building the frequency map (as shown in the original solution), which requires only one pass and is more efficient.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!