247. Strobogrammatic Number II π
Problem Description
Given an integer n
, you need to find all possible strobogrammatic numbers that have exactly n
digits.
A strobogrammatic number is a number that looks the same when rotated 180 degrees (viewed upside down). For example:
- The digit
0
rotated 180Β° still looks like0
- The digit
1
rotated 180Β° still looks like1
- The digit
8
rotated 180Β° still looks like8
- The digit
6
rotated 180Β° becomes9
- The digit
9
rotated 180Β° becomes6
Other digits (2, 3, 4, 5, 7) don't have valid rotations that form readable digits.
For a number to be strobogrammatic, when you rotate the entire number 180 degrees, it must read the same as the original. This means:
- The first digit must match with the last digit after rotation
- The second digit must match with the second-to-last digit after rotation
- And so on...
For example:
69
is strobogrammatic because when rotated,6
becomes9
and9
becomes6
, giving us69
when read from left to right88
is strobogrammatic because8
rotated is still8
818
is strobogrammatic because the middle1
stays as1
, and the outer8
s remain as8
s
You should return all valid strobogrammatic numbers of length n
. The numbers can be returned in any order.
Note: Leading zeros are not allowed in the final result (except for the number "0" itself when n=1
).
Intuition
The key insight is that strobogrammatic numbers have a symmetric structure. When building a strobogrammatic number, we need to ensure that digits on opposite ends form valid pairs when one is rotated 180 degrees.
Let's think about what pairs of digits work:
(0, 0)
- zero rotated is still zero(1, 1)
- one rotated is still one(6, 9)
- six rotated becomes nine(8, 8)
- eight rotated is still eight(9, 6)
- nine rotated becomes six
This symmetric property suggests we can build these numbers from the outside in. If we have a strobogrammatic number of length n-2
, we can create strobogrammatic numbers of length n
by wrapping valid digit pairs around it.
For example, if "1"
is a strobogrammatic number of length 1, we can create length 3 numbers by adding pairs:
"111"
(wrapping with(1,1)
)"818"
(wrapping with(8,8)
)"619"
(wrapping with(6,9)
)"916"
(wrapping with(9,6)
)
This naturally leads to a recursive approach:
- Base cases: For length 0, we have an empty string. For length 1, only
"0"
,"1"
, and"8"
work (since they look the same when rotated). - Recursive case: For length
n
, take all strobogrammatic numbers of lengthn-2
and wrap each valid digit pair around them.
There's one special consideration: we can wrap (0,0)
around inner numbers, but this creates leading zeros. Leading zeros are only valid for intermediate recursive calls, not for the final result. So we only add "0...0"
wrapping when we're building intermediate results (when u != n
), not when building the final answer of length n
.
This recursive structure naturally handles all cases - odd lengths will have a middle digit that must be self-symmetric (0
, 1
, or 8
), while even lengths will be built entirely from pairs.
Solution Approach
The solution implements a recursive approach using a helper function dfs(u)
that generates all strobogrammatic numbers of length u
.
Base Cases:
- When
u = 0
: Return['']
(list with empty string) - this serves as the foundation for building longer numbers - When
u = 1
: Return['0', '1', '8']
- these are the only single digits that look the same when rotated 180 degrees
Recursive Case (u > 1):
- First, recursively get all strobogrammatic numbers of length
u - 2
by callingdfs(u - 2)
- For each number
v
from the previous step, wrap it with valid digit pairs:- Wrap with
('1', '1')
to create'1' + v + '1'
- Wrap with
('8', '8')
to create'8' + v + '8'
- Wrap with
('6', '9')
to create'6' + v + '9'
- Wrap with
('9', '6')
to create'9' + v + '6'
- Wrap with
Handling Leading Zeros:
The pair ('0', '0')
is also valid, but we need to be careful about leading zeros:
- If
u != n
(we're building an intermediate result, not the final answer), we can add'0' + v + '0'
- If
u == n
(we're building the final result), we skip the('0', '0')
pair to avoid numbers with leading zeros
Example Walkthrough for n = 3:
dfs(3)
is called- It calls
dfs(1)
, which returns['0', '1', '8']
- For each value in
['0', '1', '8']
, we wrap with valid pairs:- For
'0'
: creates'101'
,'808'
,'609'
,'906'
- For
'1'
: creates'111'
,'818'
,'619'
,'916'
- For
'8'
: creates'181'
,'888'
,'689'
,'986'
- For
- Since
u == n
, we don't add wrappings with'0'
on both ends - Final result:
['101', '111', '181', '609', '619', '689', '808', '818', '888', '906', '916', '986']
The algorithm builds numbers layer by layer from the center outward, ensuring each layer maintains the strobogrammatic property. The time complexity is O(5^(n/2))
since we have at most 5 choices for each pair (including '00'
for intermediate steps), and we need n/2
pairs.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through finding all strobogrammatic numbers of length n = 2
.
Step 1: Initial Call
- We call
dfs(2)
to find all 2-digit strobogrammatic numbers
Step 2: Recursive Call
- Since
u = 2 > 1
, we need to calldfs(2 - 2) = dfs(0)
dfs(0)
returns['']
(base case: empty string)
Step 3: Build Numbers by Wrapping Pairs
- We take each result from
dfs(0)
(just the empty string''
) and wrap it with valid digit pairs:'1' + '' + '1'
='11'
'8' + '' + '8'
='88'
'6' + '' + '9'
='69'
'9' + '' + '6'
='96'
Step 4: Check for Leading Zeros
- Since
u = 2
equalsn = 2
(we're building the final result), we do NOT add'0' + '' + '0'
='00'
because it would have a leading zero
Final Result: ['11', '88', '69', '96']
Let's verify these are strobogrammatic:
11
β rotate 180Β° β11
β88
β rotate 180Β° β88
β69
β rotate 180Β° β69
(6β9, 9β6) β96
β rotate 180Β° β96
(9β6, 6β9) β
Now let's trace through a slightly more complex example with n = 4
:
Step 1: Call dfs(4)
Step 2: This calls dfs(2)
, which we already computed above: ['11', '88', '69', '96']
- Note: When
dfs(2)
was called as an intermediate step (u=2, but n=4), it would also include'00'
in its result, giving us['00', '11', '88', '69', '96']
Step 3: Wrap each 2-digit result with valid pairs:
- From
'00'
: β'1001'
,'8008'
,'6009'
,'9006'
- From
'11'
: β'1111'
,'8118'
,'6119'
,'9116'
- From
'88'
: β'1881'
,'8888'
,'6889'
,'9886'
- From
'69'
: β'1691'
,'8698'
,'6699'
,'9696'
- From
'96'
: β'1961'
,'8968'
,'6969'
,'9966'
Step 4: Since u = 4 = n
, we don't add '0' + ... + '0'
wrappings to avoid leading zeros
Final Result: 20 four-digit strobogrammatic numbers
Solution Implementation
1class Solution:
2 def findStrobogrammatic(self, n: int) -> List[str]:
3 """
4 Find all strobogrammatic numbers of length n.
5 A strobogrammatic number is a number that looks the same when rotated 180 degrees.
6
7 Args:
8 n: The length of the strobogrammatic numbers to find
9
10 Returns:
11 List of all strobogrammatic numbers of length n
12 """
13
14 def build_strobogrammatic(remaining_length: int) -> List[str]:
15 """
16 Recursively build strobogrammatic numbers by adding pairs from outside to inside.
17
18 Args:
19 remaining_length: The remaining length to build
20
21 Returns:
22 List of strobogrammatic numbers of the specified length
23 """
24 # Base case: no digits remaining
25 if remaining_length == 0:
26 return ['']
27
28 # Base case: single digit in the middle
29 if remaining_length == 1:
30 return ['0', '1', '8'] # Only these digits look the same when rotated
31
32 result = []
33
34 # Recursively get strobogrammatic numbers with length reduced by 2
35 inner_numbers = build_strobogrammatic(remaining_length - 2)
36
37 # For each inner number, wrap it with valid strobogrammatic pairs
38 for inner in inner_numbers:
39 # Add valid strobogrammatic pairs around the inner number
40 for left_digit, right_digit in [('1', '1'), ('8', '8'), ('6', '9'), ('9', '6')]:
41 result.append(left_digit + inner + right_digit)
42
43 # '0' can only be added if it's not the outermost layer (to avoid leading zeros)
44 if remaining_length != n:
45 result.append('0' + inner + '0')
46
47 return result
48
49 return build_strobogrammatic(n)
50
1class Solution {
2 // Define strobogrammatic digit pairs that are valid when rotated 180 degrees
3 private static final int[][] STROBOGRAMMATIC_PAIRS = {{1, 1}, {8, 8}, {6, 9}, {9, 6}};
4 private int targetLength;
5
6 /**
7 * Finds all strobogrammatic numbers of length n.
8 * A strobogrammatic number is a number that looks the same when rotated 180 degrees.
9 *
10 * @param n the desired length of strobogrammatic numbers
11 * @return a list of all strobogrammatic numbers of length n
12 */
13 public List<String> findStrobogrammatic(int n) {
14 this.targetLength = n;
15 return buildStrobogrammatic(n);
16 }
17
18 /**
19 * Recursively builds strobogrammatic numbers by adding pairs of digits
20 * to both ends of smaller strobogrammatic numbers.
21 *
22 * @param remainingLength the remaining length to build
23 * @return a list of strobogrammatic numbers of the specified length
24 */
25 private List<String> buildStrobogrammatic(int remainingLength) {
26 // Base case: empty string for length 0
27 if (remainingLength == 0) {
28 return Collections.singletonList("");
29 }
30
31 // Base case: single digits that are strobogrammatic (0, 1, 8)
32 if (remainingLength == 1) {
33 return Arrays.asList("0", "1", "8");
34 }
35
36 List<String> result = new ArrayList<>();
37
38 // Get all strobogrammatic numbers of length (remainingLength - 2)
39 List<String> innerNumbers = buildStrobogrammatic(remainingLength - 2);
40
41 // Add strobogrammatic pairs around each inner number
42 for (String inner : innerNumbers) {
43 for (int[] pair : STROBOGRAMMATIC_PAIRS) {
44 result.add(pair[0] + inner + pair[1]);
45 }
46
47 // Add zeros to both ends, but not for the outermost layer
48 // (to avoid leading zeros in the final number)
49 if (remainingLength != targetLength) {
50 result.add("0" + inner + "0");
51 }
52 }
53
54 return result;
55 }
56}
57
1class Solution {
2public:
3 // Define pairs of digits that are valid when rotated 180 degrees
4 // (left digit, right digit after rotation)
5 const vector<pair<char, char>> rotationPairs = {
6 {'1', '1'}, // 1 rotated is still 1
7 {'8', '8'}, // 8 rotated is still 8
8 {'6', '9'}, // 6 rotated becomes 9
9 {'9', '6'} // 9 rotated becomes 6
10 };
11
12 vector<string> findStrobogrammatic(int n) {
13 // Define recursive lambda function to build strobogrammatic numbers
14 function<vector<string>(int)> buildStrobogrammatic = [&](int remainingLength) {
15 // Base case: empty string
16 if (remainingLength == 0) {
17 return vector<string>{""};
18 }
19
20 // Base case: single digit (can be 0, 1, or 8 as they look the same rotated)
21 if (remainingLength == 1) {
22 return vector<string>{"0", "1", "8"};
23 }
24
25 vector<string> result;
26
27 // Recursively build smaller strobogrammatic numbers
28 vector<string> innerNumbers = buildStrobogrammatic(remainingLength - 2);
29
30 // For each inner number, wrap it with valid rotation pairs
31 for (const string& innerNumber : innerNumbers) {
32 // Add all valid rotation pairs around the inner number
33 for (const auto& [leftDigit, rightDigit] : rotationPairs) {
34 result.push_back(leftDigit + innerNumber + rightDigit);
35 }
36
37 // Special case: '0' can be added on both sides, but not as leading digit
38 // Only add '0' pairs for intermediate recursive calls, not the final length
39 if (remainingLength != n) {
40 result.push_back('0' + innerNumber + '0');
41 }
42 }
43
44 return result;
45 };
46
47 // Start the recursive process with the given length n
48 return buildStrobogrammatic(n);
49 }
50};
51
1// Define pairs of digits that are valid when rotated 180 degrees
2// (left digit, right digit after rotation)
3const rotationPairs: [string, string][] = [
4 ['1', '1'], // 1 rotated is still 1
5 ['8', '8'], // 8 rotated is still 8
6 ['6', '9'], // 6 rotated becomes 9
7 ['9', '6'] // 9 rotated becomes 6
8];
9
10function findStrobogrammatic(n: number): string[] {
11 // Define recursive function to build strobogrammatic numbers
12 const buildStrobogrammatic = (remainingLength: number): string[] => {
13 // Base case: empty string
14 if (remainingLength === 0) {
15 return [""];
16 }
17
18 // Base case: single digit (can be 0, 1, or 8 as they look the same rotated)
19 if (remainingLength === 1) {
20 return ["0", "1", "8"];
21 }
22
23 const result: string[] = [];
24
25 // Recursively build smaller strobogrammatic numbers
26 const innerNumbers = buildStrobogrammatic(remainingLength - 2);
27
28 // For each inner number, wrap it with valid rotation pairs
29 for (const innerNumber of innerNumbers) {
30 // Add all valid rotation pairs around the inner number
31 for (const [leftDigit, rightDigit] of rotationPairs) {
32 result.push(leftDigit + innerNumber + rightDigit);
33 }
34
35 // Special case: '0' can be added on both sides, but not as leading digit
36 // Only add '0' pairs for intermediate recursive calls, not the final length
37 if (remainingLength !== n) {
38 result.push('0' + innerNumber + '0');
39 }
40 }
41
42 return result;
43 };
44
45 // Start the recursive process with the given length n
46 return buildStrobogrammatic(n);
47}
48
Time and Space Complexity
Time Complexity: O(5^(n/2))
The algorithm uses recursion to build strobogrammatic numbers from the inside out. At each recursive level (which decreases by 2 each time), we have at most 5 choices for pairs to add: ('11', '88', '69', '96', '00')
. The recursion depth is n/2
since we reduce u
by 2 in each recursive call until reaching the base cases.
- For each recursive call at depth
d
, we process all strings from depthd-1
- At depth
d
, there are at most5^d
strings being generated - Each string operation (concatenation) takes
O(n)
time - Total time complexity:
O(n * 5^(n/2))
, which can be simplified toO(5^(n/2))
since the exponential term dominates
Space Complexity: O(n * 5^(n/2))
The space complexity consists of:
- Recursion call stack:
O(n/2)
=O(n)
depth - Storage for results: At the final level, we store all valid strobogrammatic numbers. The maximum number of valid n-digit strobogrammatic numbers is bounded by
5^(n/2)
, and each string has lengthn
- Intermediate results: During recursion, we maintain lists of partial results at each level, but these are dominated by the final result size
Therefore, the total space complexity is O(n * 5^(n/2))
to store all the generated strings.
Common Pitfalls
Pitfall 1: Incorrectly Handling Leading Zeros
The Problem: A common mistake is forgetting to handle the leading zero case properly. Developers might either:
- Always include
('0', '0')
pairs, resulting in invalid numbers like"00100"
for n=5 - Never include
('0', '0')
pairs, missing valid intermediate constructions needed for the recursion
Why It Happens:
The digit '0'
is strobogrammatic (looks the same when rotated), so it seems natural to always include it with other valid pairs. However, numbers shouldn't have leading zeros (except "0" itself when n=1).
Incorrect Implementation:
# WRONG: Always adding '0' pairs for inner in inner_numbers: for left_digit, right_digit in [('0', '0'), ('1', '1'), ('8', '8'), ('6', '9'), ('9', '6')]: result.append(left_digit + inner + right_digit)
Correct Solution:
The key insight is that '0'
pairs can be used in intermediate recursive calls but not in the final layer. The condition if remaining_length != n:
ensures we only add zeros when building inner layers:
for inner in inner_numbers: # Add non-zero pairs for left_digit, right_digit in [('1', '1'), ('8', '8'), ('6', '9'), ('9', '6')]: result.append(left_digit + inner + right_digit) # Only add '0' pairs for intermediate layers if remaining_length != n: # This check is crucial! result.append('0' + inner + '0')
Pitfall 2: Misunderstanding the Base Cases
The Problem: Developers might incorrectly define base cases, such as:
- Returning
['']
for both n=0 and n=1 - Forgetting that n=1 should return actual single-digit strobogrammatic numbers
- Including invalid single digits like
'6'
or'9'
alone for n=1
Why It Happens:
The digits '6'
and '9'
are part of strobogrammatic pairs, but individually they don't look the same when rotated 180 degrees. Only '0'
, '1'
, and '8'
maintain their appearance when rotated alone.
Incorrect Implementation:
# WRONG: Including 6 and 9 as valid single digits if remaining_length == 1: return ['0', '1', '6', '8', '9'] # 6 and 9 alone are NOT strobogrammatic!
Correct Solution:
if remaining_length == 0: return [''] # Foundation for building longer numbers if remaining_length == 1: return ['0', '1', '8'] # Only these look the same when rotated alone
Pitfall 3: Forgetting Edge Cases
The Problem: Not properly handling special cases like n=1 where "0" is a valid answer despite the "no leading zeros" rule.
Why It Happens: The general rule "no leading zeros" has an exception: the number "0" itself is valid when n=1. This edge case is easy to overlook.
Solution: The recursive approach naturally handles this because:
- When n=1, the function directly returns
['0', '1', '8']
from the base case - The leading zero check
if remaining_length != n:
is never reached for n=1 - This ensures "0" is included in the result for n=1 while preventing leading zeros for n>1
Which data structure is used to implement recursion?
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!