Facebook Pixel

2048. Next Greater Numerically Balanced Number

MediumHash TableMathBacktrackingCountingEnumeration
Leetcode Link

Problem Description

A number is called numerically balanced when each digit that appears in the number occurs exactly as many times as the digit's value. For example, in the number 22, the digit 2 appears exactly 2 times, making it balanced. Similarly, 333 is balanced because the digit 3 appears exactly 3 times.

Given an integer n, you need to find the smallest numerically balanced number that is strictly greater than n.

To clarify with examples:

  • 22 is balanced: digit 2 appears 2 times
  • 333 is balanced: digit 3 appears 3 times
  • 4444 is balanced: digit 4 appears 4 times
  • 122333 is balanced: digit 1 appears 1 time, digit 2 appears 2 times, digit 3 appears 3 times
  • 123 is NOT balanced: digit 1 appears 1 time (✓), but digit 2 appears only 1 time instead of 2 (✗), and digit 3 appears only 1 time instead of 3 (✗)

The task is to find the next such balanced number after the given input n.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • No: The problem involves finding a numerically balanced number, not graph traversal with nodes and edges.

Need to solve for kth smallest/largest?

  • No: We're looking for the next (smallest) balanced number after n, not the kth smallest/largest element.

Involves Linked Lists?

  • No: The problem deals with integer manipulation and digit counting, not linked list operations.

Does the problem have small constraints?

  • Yes: The constraint is n ∈ [0, 10^6], and we know that balanced numbers greater than 10^6 exist (like 1224444). This means we have a relatively small search space to explore.

Brute force / Backtracking?

  • Yes: Given the small constraints and the need to check each number for the balanced property, we can use brute force enumeration. We systematically check each number starting from n+1 until we find one that satisfies the balanced condition.

Conclusion: The flowchart correctly leads us to a brute force/backtracking approach. While this problem uses more of a brute force enumeration rather than classical backtracking with recursive exploration and pruning, the solution fits this category because:

  1. We have small enough constraints to check all possibilities
  2. We systematically explore numbers in order (n+1, n+2, n+3...)
  3. For each candidate, we validate if it meets the balanced criteria
  4. We stop and return as soon as we find the first valid number
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that numerically balanced numbers are relatively rare and follow specific patterns. For a digit d to appear exactly d times, we quickly realize that certain digits have natural limits. For instance, digit 9 would need to appear 9 times, requiring at least a 9-digit number. Similarly, digit 8 needs 8 occurrences, and so on.

This observation leads us to understand that smaller balanced numbers tend to use smaller digits. Numbers like 22, 333, 4444 are simple examples. More complex balanced numbers might combine multiple digits like 122333 (1 appears once, 2 appears twice, 3 appears three times).

Given the constraint that n ≤ 10^6, we can deduce that the search space is bounded. Even if we start from a number close to 10^6, we won't need to check too many numbers before finding a balanced one. The balanced number 1224444 (which is just above 10^6) serves as an upper bound for our search.

Why brute force works here:

  1. Limited search space: We know we'll find an answer relatively quickly
  2. No pattern formula: There's no mathematical formula to directly compute the next balanced number
  3. Simple validation: Checking if a number is balanced is straightforward - count each digit's occurrences and verify they match the digit values

The approach becomes clear: start from n + 1 and check each subsequent number until we find one that's balanced. For each candidate number, we extract its digits, count their frequencies, and verify that each digit d appears exactly d times (or doesn't appear at all).

Learn more about Math and Backtracking patterns.

Solution Approach

Following the enumeration strategy mentioned in the reference approach, we implement a straightforward brute force solution that checks each number starting from n + 1.

Algorithm Steps:

  1. Start enumeration from n + 1: We use count(n + 1) to generate an infinite sequence starting from the next number after n.

  2. For each candidate number x, we need to verify if it's balanced:

    • Create a frequency array cnt of size 10 to track how many times each digit (0-9) appears
    • Extract digits from x using division and modulo operations
  3. Digit extraction process:

    y = x  # Make a copy to preserve x
    cnt = [0] * 10  # Initialize digit count array
    while y:
        y, v = divmod(y, 10)  # Get quotient and remainder
        cnt[v] += 1  # Increment count for digit v
    • divmod(y, 10) returns both the quotient (y // 10) and remainder (y % 10)
    • The remainder gives us the last digit
    • We continue until all digits are extracted
  4. Validation check:

    all(v == 0 or i == v for i, v in enumerate(cnt))
    • For each position i in the count array (representing digits 0-9)
    • The count v at that position must either be:
      • 0: the digit doesn't appear in the number (valid)
      • Equal to i: the digit i appears exactly i times (valid)
    • If all digits satisfy this condition, the number is balanced
  5. Return the first valid number: As soon as we find a balanced number, we return it immediately since we're looking for the smallest one greater than n.

Why this works efficiently:

  • The search space is bounded - we know balanced numbers exist not too far from any given n
  • The validation is O(log x) for a number x (number of digits)
  • We stop at the first valid result, ensuring we get the smallest balanced number greater than n

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding the smallest balanced number greater than n = 20.

Step 1: Start checking from n + 1 = 21

For x = 21:

  • Extract digits: 2 and 1
  • Count array: cnt = [0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
    • Digit 1 appears 1 time
    • Digit 2 appears 1 time
  • Validation:
    • Position 0: count is 0 ✓ (digit 0 doesn't appear)
    • Position 1: count is 1, and 1 == 1 ✓ (digit 1 appears once)
    • Position 2: count is 1, but 2 ≠ 1 ✗ (digit 2 should appear twice)
  • Result: NOT balanced

Step 2: Check x = 22

For x = 22:

  • Extract digits: 2 and 2
  • Count array: cnt = [0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
    • Digit 2 appears 2 times
  • Validation:
    • Position 0: count is 0 ✓
    • Position 1: count is 0 ✓ (digit 1 doesn't appear)
    • Position 2: count is 2, and 2 == 2 ✓ (digit 2 appears twice)
    • Positions 3-9: all have count 0 ✓
  • Result: BALANCED! Return 22

The algorithm correctly identifies 22 as the smallest balanced number greater than 20.

Let's find the smallest balanced number greater than n = 49.

Starting from 50, checking each number:

For numbers 50-121, none are balanced (you can verify each fails the validation check).

When we reach x = 122:

Digit extraction process:

122 % 10 = 2 → cnt[2]++ → cnt = [0,0,1,0,0,0,0,0,0,0]
12 % 10 = 2  → cnt[2]++ → cnt = [0,0,2,0,0,0,0,0,0,0]
1 % 10 = 1   → cnt[1]++ → cnt = [0,1,2,0,0,0,0,0,0,0]

Validation check:

  • Digit 0: appears 0 times ✓
  • Digit 1: appears 1 time, 1 == 1 ✓
  • Digit 2: appears 2 times, 2 == 2 ✓
  • Digits 3-9: all appear 0 times ✓

Since all conditions are satisfied, 122 is balanced and is our answer.

Solution Implementation

1from itertools import count
2
3class Solution:
4    def nextBeautifulNumber(self, n: int) -> int:
5        # Start checking from n + 1 onwards
6        for candidate in count(n + 1):
7            # Count the frequency of each digit in the current number
8            digit_count = [0] * 10  # Array to store count of digits 0-9
9            temp_num = candidate
10          
11            # Extract each digit and count its occurrences
12            while temp_num > 0:
13                temp_num, digit = divmod(temp_num, 10)
14                digit_count[digit] += 1
15          
16            # Check if the number is beautiful:
17            # For each digit (0-9), either it doesn't appear (count is 0)
18            # or it appears exactly as many times as its value
19            is_beautiful = all(
20                count == 0 or digit == count 
21                for digit, count in enumerate(digit_count)
22            )
23          
24            if is_beautiful:
25                return candidate
26
1class Solution {
2    public int nextBeautifulNumber(int n) {
3        // Start checking from n + 1 onwards
4        for (int candidate = n + 1; ; candidate++) {
5            // Array to count occurrences of each digit (0-9)
6            int[] digitCount = new int[10];
7          
8            // Count the frequency of each digit in the current number
9            for (int temp = candidate; temp > 0; temp /= 10) {
10                digitCount[temp % 10]++;
11            }
12          
13            // Check if the number is beautiful
14            boolean isBeautiful = true;
15          
16            // A beautiful number has each digit d appearing exactly d times
17            for (int temp = candidate; temp > 0; temp /= 10) {
18                int digit = temp % 10;
19                // If digit value doesn't match its frequency, not beautiful
20                if (digit != digitCount[digit]) {
21                    isBeautiful = false;
22                    break;
23                }
24            }
25          
26            // Return the first beautiful number found
27            if (isBeautiful) {
28                return candidate;
29            }
30        }
31    }
32}
33
1class Solution {
2public:
3    int nextBeautifulNumber(int n) {
4        // Iterate through numbers starting from n + 1 until we find a beautiful number
5        for (int candidate = n + 1;; ++candidate) {
6            // Count frequency of each digit (0-9) in the current number
7            int digitFrequency[10] = {0};
8          
9            // Extract each digit and count its frequency
10            for (int temp = candidate; temp > 0; temp /= 10) {
11                int digit = temp % 10;
12                ++digitFrequency[digit];
13            }
14          
15            // Check if the current number is beautiful
16            // A beautiful number has each digit appearing exactly as many times as its value
17            bool isBeautiful = true;
18          
19            // Verify each digit appears exactly as many times as its value
20            for (int temp = candidate; temp > 0; temp /= 10) {
21                int digit = temp % 10;
22              
23                // If digit doesn't appear exactly 'digit' times, it's not beautiful
24                if (digit != digitFrequency[digit]) {
25                    isBeautiful = false;
26                    break;
27                }
28            }
29          
30            // Return the first beautiful number found
31            if (isBeautiful) {
32                return candidate;
33            }
34        }
35    }
36};
37
1/**
2 * Finds the next beautiful number greater than n.
3 * A beautiful number is one where each digit d appears exactly d times.
4 * For example: 122333 is beautiful (1 appears 1 time, 2 appears 2 times, 3 appears 3 times)
5 * @param n - The input number
6 * @returns The smallest beautiful number greater than n
7 */
8function nextBeautifulNumber(n: number): number {
9    // Start checking from n + 1 onwards
10    for (let candidate = n + 1; ; candidate++) {
11        // Initialize array to count occurrences of each digit (0-9)
12        const digitCounts: number[] = Array(10).fill(0);
13      
14        // Count the frequency of each digit in the current candidate number
15        let tempNumber = candidate;
16        while (tempNumber > 0) {
17            const lastDigit = tempNumber % 10;
18            digitCounts[lastDigit]++;
19            tempNumber = Math.floor(tempNumber / 10);
20        }
21      
22        // Check if current candidate is a beautiful number
23        let isBeautiful = true;
24        for (let digit = 0; digit < 10; digit++) {
25            // If a digit appears, it must appear exactly 'digit' times
26            // (e.g., digit 2 must appear exactly 2 times if it appears at all)
27            if (digitCounts[digit] > 0 && digitCounts[digit] !== digit) {
28                isBeautiful = false;
29                break;
30            }
31        }
32      
33        // Return the first beautiful number found
34        if (isBeautiful) {
35            return candidate;
36        }
37    }
38}
39

Time and Space Complexity

The time complexity is O(M - n), where M = 1224444 represents the maximum beautiful number we need to check. The algorithm iterates through integers starting from n + 1 until it finds a beautiful number. In the worst case, when n is close to the largest beautiful number, we need to check approximately M - n numbers. For each number x, we extract its digits (which takes O(log x) time) and verify if it's beautiful (which takes O(10) = O(1) time to check the count array). Since the maximum value we check is bounded by M = 1224444, the digit extraction is O(log M) = O(7) = O(1). Therefore, the overall time complexity is O(M - n).

The space complexity is O(1). The algorithm uses a fixed-size array cnt of length 10 to count digit occurrences, and a few integer variables (x, y, v). The space usage doesn't depend on the input size n, making it constant space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Digit 0 Validation Error

A critical pitfall is incorrectly handling digit 0. Since 0 cannot appear 0 times when it's present in the number, the validation logic v == 0 or i == v breaks for digit 0 at position i=0.

Problem Example:

  • Number 1022 would incorrectly pass validation:
    • Digit 0 appears 1 time, but i=0 requires it to appear 0 times (impossible!)
    • This creates a logical contradiction

Solution: Add special handling for digit 0:

is_beautiful = all(
    (i == 0 and count == 0) or  # Digit 0 must not appear
    (i != 0 and (count == 0 or i == count))  # Other digits
    for i, count in enumerate(digit_count)
)

2. Integer Overflow for Large Inputs

For very large values of n, continuously incrementing might cause performance issues or theoretical overflow concerns in other languages (though Python handles big integers automatically).

Solution: Add an upper bound check or use mathematical properties of balanced numbers:

# Balanced numbers have predictable patterns and maximum lengths
# The largest possible balanced number has at most 10 digits
if candidate > 9876543210:  # Theoretical maximum
    break  # No valid answer exists

3. Leading Zero Confusion

When extracting digits, ensure you're not accidentally considering leading zeros (though this doesn't happen with integer representation).

Solution: The current approach naturally avoids this since we work with integers directly, but be careful if converting to strings:

# Avoid: str(candidate).count(str(digit))
# This could misinterpret numbers in string format

4. Inefficient Enumeration Range

Starting from n+1 and checking every single number can be inefficient for large gaps between balanced numbers.

Solution: Pre-compute or use mathematical patterns to skip impossible candidates:

# Skip numbers with digits > 9 in their digit count
# For example, if we see digit 5 appearing 6+ times, skip
if any(count > 9 for count in digit_count):
    continue  # This can never be balanced
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More