LeetCode 247 - Strobogrammatic Number II
The problem asks us to generate every strobogrammatic number of a given length n. A strobogrammatic number is a number that still appears valid after being rotated 180 degrees. Not every digit works under rotation.
Difficulty: 🟡 Medium
Topics: Array, String, Recursion
Solution
Problem Understanding
The problem asks us to generate every strobogrammatic number of a given length n.
A strobogrammatic number is a number that still appears valid after being rotated 180 degrees. Not every digit works under rotation. Only the following digit mappings are valid:
| Original | Rotated |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 6 | 9 |
| 8 | 8 |
| 9 | 6 |
For example, "69" becomes "96" after rotation, which is still a valid number. Similarly, "88" remains "88".
The input is a single integer n, representing the exact length of the numbers we must generate. The output is a list of strings because some generated values may begin with zeros internally during recursion, and string manipulation is more natural for this problem.
The constraints are small enough that generating all valid combinations is feasible. Since n <= 14, the total number of valid strobogrammatic numbers is manageable. This strongly suggests a constructive generation approach rather than checking every possible number.
Several edge cases are important:
- Numbers cannot have leading zeros unless the number itself has length
1. For example,"00"is invalid forn = 2. - Odd-length numbers must have a valid middle digit that maps to itself. Only
0,1, and8satisfy this condition. - Even-length numbers do not have a center digit, so every position must be paired symmetrically.
- Recursive generation can accidentally produce invalid outermost zeros if not handled carefully.
Approaches
Brute Force Approach
A brute-force solution would generate every number with exactly n digits and check whether each one is strobogrammatic.
To verify a number, we could scan from both ends toward the center and ensure that every digit pair belongs to the valid rotation mappings. For example, if the left digit is 6, the corresponding right digit must be 9.
This approach is correct because it exhaustively checks every candidate. However, it is extremely inefficient. There are 10^n possible numbers of length n, which becomes enormous even for moderate values of n. Since n can be as large as 14, brute force is completely impractical.
Optimal Recursive Construction
The key observation is that strobogrammatic numbers are highly structured.
Instead of generating every possible number and filtering valid ones, we can directly construct only valid strobogrammatic numbers.
A strobogrammatic number is built symmetrically from the outside inward. If we place a digit on the left side, the right side is automatically determined by the rotation mapping.
For example:
- If we place
6on the left, we must place9on the right. - If we place
1on the left, we must place1on the right.
This naturally leads to recursion:
- Build smaller strobogrammatic numbers first.
- Surround them with valid digit pairs.
- Continue until the desired length is reached.
This avoids generating invalid candidates entirely.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(10^n × n) | O(n) | Generates every n-digit number and validates each |
| Optimal | O(5^(n/2)) | O(5^(n/2)) | Constructs only valid strobogrammatic numbers recursively |
Algorithm Walkthrough
Optimal Recursive Algorithm
- Define all valid strobogrammatic digit pairs.
We store the following pairs:
('0', '0')
('1', '1')
('6', '9')
('8', '8')
('9', '6')
These represent the only valid outer digit combinations. 2. Create a recursive helper function.
The helper function generates all strobogrammatic numbers of length currentLength.
We also keep track of the original target length so we can avoid leading zeros in the outermost layer. 3. Define the base cases.
If currentLength == 0, return [""].
This represents an empty center and allows recursive expansion outward.
If currentLength == 1, return ["0", "1", "8"].
These are the only digits that remain valid when rotated individually. 4. Recursively generate smaller strobogrammatic numbers.
We call the helper on currentLength - 2.
This generates all valid inner sections. 5. Expand each smaller result using valid pairs.
For every inner string:
- Add
"0"and"0"around it - Add
"1"and"1"around it - Add
"6"and"9"around it - Add
"8"and"8"around it - Add
"9"and"6"around it
- Prevent leading zeros.
When constructing the outermost layer, we must not place "0" at the front.
We detect this using:
if currentLength != originalLength
Inner recursive layers are allowed to contain zeros because they are not leading digits in the final result. 7. Return the constructed list.
After processing all inner strings and all valid pairs, the helper returns every valid strobogrammatic number for that length.
Why it works
The algorithm works because every strobogrammatic number must satisfy rotational symmetry. Each outer digit uniquely determines its mirrored counterpart. By recursively building the inner portion first and then surrounding it with valid digit pairs, the algorithm guarantees that every generated number is valid. Since every possible valid pair is explored at every level, no valid strobogrammatic number is missed.
Python Solution
from typing import List
class Solution:
def findStrobogrammatic(self, n: int) -> List[str]:
pairs = [
('0', '0'),
('1', '1'),
('6', '9'),
('8', '8'),
('9', '6')
]
def build(current_length: int, target_length: int) -> List[str]:
if current_length == 0:
return [""]
if current_length == 1:
return ["0", "1", "8"]
inner_numbers = build(current_length - 2, target_length)
result = []
for middle in inner_numbers:
for left, right in pairs:
if current_length == target_length and left == '0':
continue
result.append(left + middle + right)
return result
return build(n, n)
The implementation starts by defining all valid strobogrammatic digit pairs. These pairs encode the rotational symmetry rules directly.
The recursive build function generates all valid strobogrammatic numbers for a given length. The parameter current_length represents the size currently being constructed, while target_length preserves the original requested size.
The base case for length 0 returns an empty string, which acts as a neutral center during recursive expansion. The base case for length 1 returns the only valid self-rotating digits.
For larger lengths, the function recursively generates all smaller valid inner strings. Each inner string is then wrapped with every valid digit pair.
The condition preventing leading zeros is crucial. We only skip the ('0', '0') pair when constructing the outermost layer. Inner layers may still contain zeros safely.
Finally, the function returns all constructed strings.
Go Solution
func findStrobogrammatic(n int) []string {
pairs := [][]string{
{"0", "0"},
{"1", "1"},
{"6", "9"},
{"8", "8"},
{"9", "6"},
}
var build func(int, int) []string
build = func(currentLength int, targetLength int) []string {
if currentLength == 0 {
return []string{""}
}
if currentLength == 1 {
return []string{"0", "1", "8"}
}
innerNumbers := build(currentLength-2, targetLength)
result := []string{}
for _, middle := range innerNumbers {
for _, pair := range pairs {
left := pair[0]
right := pair[1]
if currentLength == targetLength && left == "0" {
continue
}
result = append(result, left+middle+right)
}
}
return result
}
return build(n, n)
}
The Go solution follows the same recursive structure as the Python implementation. Slices are used to store generated strings dynamically.
One notable difference is that Go requires explicit function variable declarations for recursive anonymous functions. The build function is declared first and assigned afterward.
Go strings are immutable, just like Python strings, so concatenation creates new strings safely.
No integer overflow concerns exist because the algorithm operates entirely on strings rather than numeric conversions.
Worked Examples
Example 1
Input: n = 2
We call:
build(2, 2)
The recursive call becomes:
build(0, 2)
Since currentLength == 0, return:
[""]
Now expand using all valid pairs except leading zero.
| Inner String | Pair | Result |
|---|---|---|
| "" | ("1","1") | "11" |
| "" | ("6","9") | "69" |
| "" | ("8","8") | "88" |
| "" | ("9","6") | "96" |
We skip:
("0","0")
because it would create "00".
Final output:
["11", "69", "88", "96"]
Example 2
Input: n = 1
We call:
build(1, 1)
Since currentLength == 1, directly return:
["0", "1", "8"]
Final output:
["0", "1", "8"]
Additional Example
Input: n = 3
Start with:
build(3, 3)
Recursive call:
build(1, 3)
Base case returns:
["0", "1", "8"]
Now expand each center digit.
| Center | Pair | Constructed Number |
|---|---|---|
| "0" | ("1","1") | "101" |
| "0" | ("6","9") | "609" |
| "0" | ("8","8") | "808" |
| "0" | ("9","6") | "906" |
| "1" | ("1","1") | "111" |
| "1" | ("6","9") | "619" |
| "1" | ("8","8") | "818" |
| "1" | ("9","6") | "916" |
| "8" | ("1","1") | "181" |
| "8" | ("6","9") | "689" |
| "8" | ("8","8") | "888" |
| "8" | ("9","6") | "986" |
Final output contains all 12 valid numbers.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(5^(n/2)) | Each recursive layer branches into at most 5 valid digit pairs |
| Space | O(5^(n/2)) | Required to store all generated strobogrammatic numbers |
The recursion depth is at most n / 2, since each step fills two positions. At every level, there are at most 5 branching choices. Therefore, the total number of generated strings is proportional to 5^(n/2).
The space complexity is dominated by the output itself, since all valid strobogrammatic numbers must be stored and returned.
Test Cases
from typing import List
class Solution:
def findStrobogrammatic(self, n: int) -> List[str]:
pairs = [
('0', '0'),
('1', '1'),
('6', '9'),
('8', '8'),
('9', '6')
]
def build(current_length: int, target_length: int) -> List[str]:
if current_length == 0:
return [""]
if current_length == 1:
return ["0", "1", "8"]
inner_numbers = build(current_length - 2, target_length)
result = []
for middle in inner_numbers:
for left, right in pairs:
if current_length == target_length and left == '0':
continue
result.append(left + middle + right)
return result
return build(n, n)
solution = Solution()
# Example case for n = 1
assert sorted(solution.findStrobogrammatic(1)) == ["0", "1", "8"]
# Example case for n = 2
assert sorted(solution.findStrobogrammatic(2)) == ["11", "69", "88", "96"]
# Small odd length
assert sorted(solution.findStrobogrammatic(3)) == sorted([
"101", "111", "181",
"609", "619", "689",
"808", "818", "888",
"906", "916", "986"
])
# Ensure no leading zeros for n > 1
result = solution.findStrobogrammatic(4)
assert all(not num.startswith("0") for num in result)
# Verify all generated numbers have correct length
result = solution.findStrobogrammatic(5)
assert all(len(num) == 5 for num in result)
# Verify rotational symmetry property
mapping = {
'0': '0',
'1': '1',
'6': '9',
'8': '8',
'9': '6'
}
for number in solution.findStrobogrammatic(5):
rotated = "".join(mapping[d] for d in reversed(number))
assert rotated == number
# Boundary case: largest allowed n
result = solution.findStrobogrammatic(14)
assert all(len(num) == 14 for num in result)
Test Summary
| Test | Why |
|---|---|
n = 1 |
Validates single-digit base case |
n = 2 |
Validates smallest even-length construction |
n = 3 |
Validates odd-length recursive construction |
| No leading zeros | Ensures outermost zero handling is correct |
| Length verification | Ensures recursion builds exact target size |
| Rotational symmetry validation | Confirms every output is truly strobogrammatic |
n = 14 |
Tests upper constraint boundary |
Edge Cases
Single-Digit Inputs
When n = 1, the number consists of only a center digit. Many implementations mistakenly allow digits like 6 or 9 in the center, but these digits rotate into each other rather than themselves. Only 0, 1, and 8 are valid single-digit strobogrammatic numbers. The implementation handles this explicitly in the current_length == 1 base case.
Leading Zeros
A very common bug is accidentally generating numbers such as "00" or "0110" for multi-digit inputs. These are invalid because standard numeric representations cannot begin with zero. The implementation prevents this by skipping the ("0", "0") pair only when constructing the outermost layer.
Odd-Length Numbers
Odd-length strobogrammatic numbers require a valid center digit. Some recursive solutions incorrectly assume every position must be paired, which fails for odd lengths. The implementation handles this naturally through the current_length == 1 base case, allowing recursion to terminate with a single valid middle digit.
Large Inputs
For n = 14, the number of generated strings becomes large. A brute-force approach would be completely infeasible because it would attempt to inspect trillions of candidates. The recursive constructive approach remains efficient because it generates only valid numbers and avoids unnecessary computation.