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 still0
- The digit
1
rotated 180 degrees is still1
- The digit
8
rotated 180 degrees is still8
- The digit
6
rotated 180 degrees becomes9
- The digit
9
rotated 180 degrees becomes6
- 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.
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 (only0
,1
, or8
work) - If
n
is even, we don't need a middle digit
We can recursively construct all strobogrammatic numbers of length n
by:
- Starting with strobogrammatic numbers of length
n-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:
- Get all strobogrammatic numbers of length
u - 2
by callingdfs(u - 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)
- Special handling for
'0' + v + '0'
: Only add this ifu != n
(not the outermost layer) to avoid leading zeros
Main Algorithm:
- Extract the lengths
a
andb
from the input stringslow
andhigh
- Convert
low
andhigh
to integers for numerical comparison - Iterate through all possible lengths from
a
tob + 1
:- For each length
n
, generate all strobogrammatic numbers usingdfs(n)
- Convert each generated string to an integer
- Check if it falls within the range
[low, high]
- If yes, increment the counter
- For each length
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 EvaluatorExample Walkthrough
Let's walk through finding all strobogrammatic numbers in the range [50, 100]
.
Step 1: Determine the length range
low = "50"
has length 2high = "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
tob
, which isO(b - a + 1)
iterations. - For each length
n
, thedfs(n)
function is called which generates all strobogrammatic numbers of lengthn
. - The recurrence relation for counting strobogrammatic numbers is approximately
T(n) = 5 * T(n-2)
forn > 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 lengthn
. - In the worst case, when
n = b
, we generateO(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 toO((b - a + 1) * 5^(b/2))
sinceb
is typically much smaller than the exponential term.
Space Complexity: O(5^(b/2) * b)
- The
dfs
function uses recursion with maximum depthO(b/2)
, contributingO(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 approximately5^(b/2)
strings, each of lengthb
. - Therefore, the space needed to store all strobogrammatic numbers of length
b
isO(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.
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
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
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!