Facebook Pixel

248. Strobogrammatic Number III πŸ”’

Problem Description

You are given two strings low and high that represent two integers where low <= high. Your task is to count how many strobogrammatic numbers exist within the range [low, high] (inclusive).

A strobogrammatic number is a number that looks the same when rotated 180 degrees (viewed upside down).

For example:

  • The digit 0 rotated 180 degrees is still 0
  • The digit 1 rotated 180 degrees is still 1
  • The digit 8 rotated 180 degrees is still 8
  • The digit 6 rotated 180 degrees becomes 9
  • The digit 9 rotated 180 degrees becomes 6
  • Other digits (2, 3, 4, 5, 7) don't have valid rotations

So numbers like 69, 88, 181, and 9006 are strobogrammatic because when you rotate them 180 degrees, they look the same. For instance, 69 becomes 69 (the 6 becomes 9 and the 9 becomes 6 when rotated), and 181 remains 181 (1 stays 1, 8 stays 8, and 1 stays 1).

The function should return the total count of strobogrammatic numbers that fall within the given range.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To build strobogrammatic numbers, we need to think about which digits can be used and how they must be arranged. Only the digits 0, 1, 6, 8, 9 can appear in a strobogrammatic number since these are the only digits that have valid 180-degree rotations.

The key insight is that strobogrammatic numbers have a symmetric structure. When we rotate the number 180 degrees, the first digit becomes the last digit (rotated), the second digit becomes the second-to-last digit (rotated), and so on. This means we can build these numbers from the outside in - by adding pairs of digits to both ends.

For a strobogrammatic number of length n:

  • If n is odd, there must be a middle digit that looks the same when rotated (only 0, 1, or 8 work)
  • If n is even, we don't need a middle digit

We can recursively construct all strobogrammatic numbers of length n by:

  1. Starting with strobogrammatic numbers of length n-2
  2. Wrapping them with valid digit pairs: (1,1), (8,8), (6,9), (9,6), and (0,0)

The pair (0,0) is special - we can't use it for the outermost digits because that would create a number with leading zeros (like 00110), which isn't valid. However, we can use (0,0) for inner positions.

By building numbers of each possible length between the lengths of low and high, and then filtering those that fall within the actual numeric range, we can count all valid strobogrammatic numbers in the given range.

The base cases are straightforward:

  • Length 0: empty string ""
  • Length 1: only "0", "1", "8" (single digits that look the same when rotated)

From these base cases, we can build up all longer strobogrammatic numbers by recursively adding valid pairs around shorter ones.

Solution Approach

The solution uses a recursive approach with a helper function dfs(u) that generates all strobogrammatic numbers of length u.

Base Cases:

  • When u = 0: Return [''] (empty string list)
  • When u = 1: Return ['0', '1', '8'] (single digits that remain the same when rotated)

Recursive Case: For lengths greater than 1, we:

  1. Get all strobogrammatic numbers of length u - 2 by calling dfs(u - 2)
  2. For each number v from that list, wrap it with valid digit pairs:
    • '1' + v + '1' (pair 11)
    • '8' + v + '8' (pair 88)
    • '6' + v + '9' (pair 69)
    • '9' + v + '6' (pair 96)
  3. Special handling for '0' + v + '0': Only add this if u != n (not the outermost layer) to avoid leading zeros

Main Algorithm:

  1. Extract the lengths a and b from the input strings low and high
  2. Convert low and high to integers for numerical comparison
  3. Iterate through all possible lengths from a to b + 1:
    • For each length n, generate all strobogrammatic numbers using dfs(n)
    • Convert each generated string to an integer
    • Check if it falls within the range [low, high]
    • If yes, increment the counter

Example Walkthrough: If we want strobogrammatic numbers of length 3:

  • Start with dfs(1) which returns ['0', '1', '8']
  • Wrap each with valid pairs:
    • '0' becomes '101', '808', '609', '906'
    • '1' becomes '111', '818', '619', '916'
    • '8' becomes '181', '888', '689', '986'

The time complexity is O(2^{n+2} Γ— log n) where the exponential factor comes from the branching in the recursion (each level can add up to 5 pairs), and the logarithmic factor comes from string to integer conversions.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through finding all strobogrammatic numbers in the range [50, 100].

Step 1: Determine the length range

  • low = "50" has length 2
  • high = "100" has length 3
  • We need to generate strobogrammatic numbers of lengths 2 and 3

Step 2: Generate length 2 strobogrammatic numbers

Starting with dfs(2):

  • First, we call dfs(0) which returns ['']
  • For each empty string, we wrap with valid pairs:
    • '1' + '' + '1' = '11'
    • '8' + '' + '8' = '88'
    • '6' + '' + '9' = '69'
    • '9' + '' + '6' = '96'
  • Since u = n = 2 (outermost layer), we skip '0' + '' + '0' to avoid leading zeros
  • Result: ['11', '88', '69', '96']

Step 3: Generate length 3 strobogrammatic numbers

Starting with dfs(3):

  • First, we call dfs(1) which returns ['0', '1', '8']

  • For each single digit, we wrap with valid pairs:

    For '0':

    • '1' + '0' + '1' = '101'
    • '8' + '0' + '8' = '808'
    • '6' + '0' + '9' = '609'
    • '9' + '0' + '6' = '906'

    For '1':

    • '1' + '1' + '1' = '111'
    • '8' + '1' + '8' = '818'
    • '6' + '1' + '9' = '619'
    • '9' + '1' + '6' = '916'

    For '8':

    • '1' + '8' + '1' = '181'
    • '8' + '8' + '8' = '888'
    • '6' + '8' + '9' = '689'
    • '9' + '8' + '6' = '986'
  • Since u = n = 3 (outermost layer), we don't add '0' + v + '0' pairs

  • Result: ['101', '808', '609', '906', '111', '818', '619', '916', '181', '888', '689', '986']

Step 4: Filter by range [50, 100]

From length 2 numbers:

  • 11 β†’ Not in range (< 50)
  • 88 β†’ In range βœ“
  • 69 β†’ In range βœ“
  • 96 β†’ In range βœ“

From length 3 numbers:

  • All are > 100, so none are in range

Final Answer: 3 (the numbers 88, 69, and 96)

This example demonstrates how the recursive approach builds strobogrammatic numbers layer by layer, avoiding leading zeros, and then filters the results to count only those within the specified range.

Solution Implementation

1class Solution:
2    def strobogrammaticInRange(self, low: str, high: str) -> int:
3        def generate_strobogrammatic_numbers(length: int, target_length: int) -> list[str]:
4            """
5            Recursively generate all strobogrammatic numbers of given length.
6          
7            Args:
8                length: Current length to generate
9                target_length: The final target length (used to avoid leading zeros)
10          
11            Returns:
12                List of strobogrammatic number strings
13            """
14            # Base case: empty string
15            if length == 0:
16                return ['']
17          
18            # Base case: single digit strobogrammatic numbers
19            if length == 1:
20                return ['0', '1', '8']
21          
22            result = []
23          
24            # Get all strobogrammatic numbers of length-2
25            middle_numbers = generate_strobogrammatic_numbers(length - 2, target_length)
26          
27            # For each middle number, wrap it with valid strobogrammatic pairs
28            for middle in middle_numbers:
29                # Add valid strobogrammatic digit pairs
30                for left_digit, right_digit in [('1', '1'), ('8', '8'), ('6', '9'), ('9', '6')]:
31                    result.append(left_digit + middle + right_digit)
32              
33                # Add '0' pair only if it won't create leading zeros
34                # (i.e., when current length is not the target length)
35                if length != target_length:
36                    result.append('0' + middle + '0')
37          
38            return result
39      
40        # Get the lengths of the boundary strings
41        min_length = len(low)
42        max_length = len(high)
43      
44        # Convert string boundaries to integers for comparison
45        low_value = int(low)
46        high_value = int(high)
47      
48        # Count valid strobogrammatic numbers
49        count = 0
50      
51        # Generate and check strobogrammatic numbers for each possible length
52        for current_length in range(min_length, max_length + 1):
53            strobogrammatic_nums = generate_strobogrammatic_numbers(current_length, current_length)
54          
55            # Count numbers within the given range
56            for num_str in strobogrammatic_nums:
57                num_value = int(num_str)
58                if low_value <= num_value <= high_value:
59                    count += 1
60      
61        return count
62
1class Solution {
2    // Define strobogrammatic digit pairs that form valid rotations
3    private static final int[][] STROBOGRAMMATIC_PAIRS = {{1, 1}, {8, 8}, {6, 9}, {9, 6}};
4    private int targetLength;
5
6    /**
7     * Counts all strobogrammatic numbers within the given range [low, high]
8     * A strobogrammatic number is a number that looks the same when rotated 180 degrees
9     * 
10     * @param low The lower bound of the range (inclusive)
11     * @param high The upper bound of the range (inclusive)
12     * @return The count of strobogrammatic numbers in the range
13     */
14    public int strobogrammaticInRange(String low, String high) {
15        int lowLength = low.length();
16        int highLength = high.length();
17        long lowerBound = Long.parseLong(low);
18        long upperBound = Long.parseLong(high);
19        int count = 0;
20      
21        // Generate strobogrammatic numbers for each possible length
22        for (targetLength = lowLength; targetLength <= highLength; ++targetLength) {
23            List<String> strobogrammaticNumbers = generateStrobogrammaticNumbers(targetLength);
24          
25            // Check if each generated number falls within the range
26            for (String number : strobogrammaticNumbers) {
27                long numericValue = Long.parseLong(number);
28                if (lowerBound <= numericValue && numericValue <= upperBound) {
29                    ++count;
30                }
31            }
32        }
33      
34        return count;
35    }
36
37    /**
38     * Recursively generates all strobogrammatic numbers of a given length
39     * 
40     * @param remainingLength The length of strobogrammatic numbers to generate
41     * @return List of all strobogrammatic numbers of the specified length
42     */
43    private List<String> generateStrobogrammaticNumbers(int remainingLength) {
44        // Base case: empty string for length 0
45        if (remainingLength == 0) {
46            return Collections.singletonList("");
47        }
48      
49        // Base case: single digit strobogrammatic numbers
50        if (remainingLength == 1) {
51            return Arrays.asList("0", "1", "8");
52        }
53      
54        List<String> result = new ArrayList<>();
55      
56        // Recursively build numbers by adding pairs to both ends
57        List<String> innerNumbers = generateStrobogrammaticNumbers(remainingLength - 2);
58        for (String inner : innerNumbers) {
59            // Add valid strobogrammatic pairs around the inner number
60            for (int[] pair : STROBOGRAMMATIC_PAIRS) {
61                result.add(pair[0] + inner + pair[1]);
62            }
63          
64            // Add zeros only for inner recursions (not for the final length)
65            // to avoid leading zeros in the final number
66            if (remainingLength != targetLength) {
67                result.add("0" + inner + "0");
68            }
69        }
70      
71        return result;
72    }
73}
74
1using ll = long long;
2
3class Solution {
4public:
5    // Valid strobogrammatic digit pairs (left digit, right digit when rotated 180Β°)
6    const vector<pair<char, char>> DIGIT_PAIRS = {
7        {'1', '1'}, {'8', '8'}, {'6', '9'}, {'9', '6'}
8    };
9
10    int strobogrammaticInRange(string low, string high) {
11        int targetLength;
12      
13        // Recursive function to generate all strobogrammatic numbers of given length
14        function<vector<string>(int)> generateStrobogrammatic = [&](int remainingLength) {
15            // Base case: empty string
16            if (remainingLength == 0) {
17                return vector<string>{""};
18            }
19            // Base case: single digit (only 0, 1, 8 are valid middle digits)
20            if (remainingLength == 1) {
21                return vector<string>{"0", "1", "8"};
22            }
23          
24            vector<string> result;
25            // Get all valid strobogrammatic numbers with length-2
26            vector<string> innerNumbers = generateStrobogrammatic(remainingLength - 2);
27          
28            // Build new numbers by adding digit pairs around inner numbers
29            for (const auto& inner : innerNumbers) {
30                // Add non-zero digit pairs
31                for (const auto& [leftDigit, rightDigit] : DIGIT_PAIRS) {
32                    result.push_back(leftDigit + inner + rightDigit);
33                }
34                // Add '0' pair only if not building the outermost layer (to avoid leading zeros)
35                if (remainingLength != targetLength) {
36                    result.push_back('0' + inner + '0');
37                }
38            }
39            return result;
40        };
41
42        int lowLength = low.size();
43        int highLength = high.size();
44        int count = 0;
45        ll lowerBound = stoll(low);
46        ll upperBound = stoll(high);
47      
48        // Generate and count valid strobogrammatic numbers for each length
49        for (targetLength = lowLength; targetLength <= highLength; ++targetLength) {
50            vector<string> strobogrammaticNumbers = generateStrobogrammatic(targetLength);
51          
52            // Count numbers within the given range
53            for (const auto& numStr : strobogrammaticNumbers) {
54                ll numValue = stoll(numStr);
55                if (lowerBound <= numValue && numValue <= upperBound) {
56                    ++count;
57                }
58            }
59        }
60      
61        return count;
62    }
63};
64
1// Type alias for long long equivalent (using BigInt for large numbers)
2type ll = bigint;
3
4// Valid strobogrammatic digit pairs (left digit, right digit when rotated 180Β°)
5const DIGIT_PAIRS: Array<[string, string]> = [
6    ['1', '1'], ['8', '8'], ['6', '9'], ['9', '6']
7];
8
9function strobogrammaticInRange(low: string, high: string): number {
10    let targetLength: number;
11  
12    // Recursive function to generate all strobogrammatic numbers of given length
13    const generateStrobogrammatic = (remainingLength: number): string[] => {
14        // Base case: empty string
15        if (remainingLength === 0) {
16            return [""];
17        }
18        // Base case: single digit (only 0, 1, 8 are valid middle digits)
19        if (remainingLength === 1) {
20            return ["0", "1", "8"];
21        }
22      
23        const result: string[] = [];
24        // Get all valid strobogrammatic numbers with length-2
25        const innerNumbers: string[] = generateStrobogrammatic(remainingLength - 2);
26      
27        // Build new numbers by adding digit pairs around inner numbers
28        for (const inner of innerNumbers) {
29            // Add non-zero digit pairs
30            for (const [leftDigit, rightDigit] of DIGIT_PAIRS) {
31                result.push(leftDigit + inner + rightDigit);
32            }
33            // Add '0' pair only if not building the outermost layer (to avoid leading zeros)
34            if (remainingLength !== targetLength) {
35                result.push('0' + inner + '0');
36            }
37        }
38        return result;
39    };
40
41    const lowLength: number = low.length;
42    const highLength: number = high.length;
43    let count: number = 0;
44    const lowerBound: ll = BigInt(low);
45    const upperBound: ll = BigInt(high);
46  
47    // Generate and count valid strobogrammatic numbers for each length
48    for (targetLength = lowLength; targetLength <= highLength; targetLength++) {
49        const strobogrammaticNumbers: string[] = generateStrobogrammatic(targetLength);
50      
51        // Count numbers within the given range
52        for (const numStr of strobogrammaticNumbers) {
53            const numValue: ll = BigInt(numStr);
54            if (lowerBound <= numValue && numValue <= upperBound) {
55                count++;
56            }
57        }
58    }
59  
60    return count;
61}
62

Time and Space Complexity

Time Complexity: O((b - a + 1) * 5^(b/2)) where a is the length of the low string and b is the length of the high string.

  • The outer loop iterates through each possible length from a to b, which is O(b - a + 1) iterations.
  • For each length n, the dfs(n) function is called which generates all strobogrammatic numbers of length n.
  • The recurrence relation for counting strobogrammatic numbers is approximately T(n) = 5 * T(n-2) for n > 2 (since we add 5 pairs for each recursive call - '00', '11', '88', '69', '96', though '00' is conditional).
  • This gives us approximately 5^(n/2) strobogrammatic numbers for length n.
  • In the worst case, when n = b, we generate O(5^(b/2)) numbers.
  • For each generated number, we perform an integer conversion and comparison which takes O(b) time.
  • Overall time complexity: O((b - a + 1) * 5^(b/2) * b), which simplifies to O((b - a + 1) * 5^(b/2)) since b is typically much smaller than the exponential term.

Space Complexity: O(5^(b/2) * b)

  • The dfs function uses recursion with maximum depth O(b/2), contributing O(b) to the call stack.
  • At each recursion level, we store intermediate results. The largest storage occurs at the top level when n = b.
  • For length b, we store approximately 5^(b/2) strings, each of length b.
  • Therefore, the space needed to store all strobogrammatic numbers of length b is O(5^(b/2) * b).
  • The space for storing the answer list ans in each recursive call is dominated by the final result at the top level.
  • Overall space complexity: O(5^(b/2) * b)

Common Pitfalls

1. Leading Zeros Issue

The most critical pitfall in this problem is incorrectly handling leading zeros. The recursive function might generate numbers like "00", "000", or "0110" which are not valid integers.

Why it happens: When building strobogrammatic numbers recursively, we can add '0' + middle + '0' as a valid pair. However, if this happens at the outermost layer (when length == target_length), it creates invalid numbers with leading zeros.

The Fix: Only add the '0' + middle + '0' pair when we're NOT at the outermost layer:

# Correct approach
if length != target_length:
    result.append('0' + middle + '0')

2. String vs Integer Comparison Confusion

Another common mistake is comparing string representations directly instead of integer values:

Wrong approach:

# This would fail because string comparison is lexicographic
if low <= num_str <= high:  # WRONG!
    count += 1

Correct approach:

# Convert to integers for numerical comparison
low_value = int(low)
high_value = int(high)
num_value = int(num_str)
if low_value <= num_value <= high_value:
    count += 1

3. Off-by-One Error in Length Iteration

Forgetting to include the maximum length in the range:

Wrong:

for current_length in range(min_length, max_length):  # Excludes max_length

Correct:

for current_length in range(min_length, max_length + 1):  # Includes max_length

4. Not Handling Edge Cases for Single-Digit Numbers

When the range includes single-digit numbers, ensure the base case correctly identifies which single digits are strobogrammatic (0, 1, 8) and excludes invalid ones (2, 3, 4, 5, 7).

5. Memory Inefficiency from Redundant Calculations

The current solution recalculates strobogrammatic numbers for each length independently. If you're calling this function multiple times or with large ranges, consider memoization to cache previously computed results for specific lengths.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

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

Load More