Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

248. Strobogrammatic Number III - Leetcode Solution

Code Implementation

class Solution:
    def strobogrammaticInRange(self, low: str, high: str) -> int:
        self.count = 0
        pairs = [('0','0'), ('1','1'), ('6','9'), ('8','8'), ('9','6')]
        
        def dfs(s, length):
            if len(s) > len(high) or (len(s) > 1 and s[0] == '0'):
                return
            if len(s) >= len(low) and len(s) <= len(high):
                if (len(s) == len(low) and s < low) or (len(s) == len(high) and s > high):
                    pass
                else:
                    if not (len(s) > 1 and s[0] == '0'):
                        self.count += 1
            if len(s) + 2 > len(high):
                return
            for a, b in pairs:
                dfs(a + s + b, length)
        
        for length in range(len(low), len(high)+1):
            if length == 1:
                for c in ['0','1','8']:
                    if (len(low) == 1 and c < low) or (len(high) == 1 and c > high):
                        continue
                    self.count += 1
            else:
                for a, b in pairs:
                    if a == '0':
                        continue
                    dfs(a + b, length)
        return self.count
      
class Solution {
public:
    int strobogrammaticInRange(string low, string high) {
        int count = 0;
        vector<pair<char, char>> pairs = {{'0','0'}, {'1','1'}, {'6','9'}, {'8','8'}, {'9','6'}};
        function<void(string, int)> dfs = [&](string s, int length) {
            if (s.length() > high.length() || (s.length() > 1 && s[0] == '0'))
                return;
            if (s.length() >= low.length() && s.length() <= high.length()) {
                if ((s.length() == low.length() && s < low) || (s.length() == high.length() && s > high)) {
                    // do nothing
                } else {
                    if (!(s.length() > 1 && s[0] == '0'))
                        count++;
                }
            }
            if (s.length() + 2 > high.length()) return;
            for (auto &p : pairs) {
                dfs(p.first + s + p.second, length);
            }
        };
        for (int length = low.length(); length <= high.length(); ++length) {
            if (length == 1) {
                for (char c : {'0','1','8'}) {
                    if ((low.length() == 1 && c < low[0]) || (high.length() == 1 && c > high[0]))
                        continue;
                    count++;
                }
            } else {
                for (auto &p : pairs) {
                    if (p.first == '0') continue;
                    dfs(string(1, p.first) + string(1, p.second), length);
                }
            }
        }
        return count;
    }
};
      
class Solution {
    private int count = 0;
    private final char[][] pairs = {{'0','0'}, {'1','1'}, {'6','9'}, {'8','8'}, {'9','6'}};

    public int strobogrammaticInRange(String low, String high) {
        for (int length = low.length(); length <= high.length(); length++) {
            if (length == 1) {
                for (char c : new char[]{'0','1','8'}) {
                    if ((low.length() == 1 && c < low.charAt(0)) || (high.length() == 1 && c > high.charAt(0)))
                        continue;
                    count++;
                }
            } else {
                for (char[] p : pairs) {
                    if (p[0] == '0') continue;
                    dfs(new StringBuilder().append(p[0]).append(p[1]).toString(), length, low, high);
                }
            }
        }
        return count;
    }

    private void dfs(String s, int length, String low, String high) {
        if (s.length() > high.length() || (s.length() > 1 && s.charAt(0) == '0'))
            return;
        if (s.length() >= low.length() && s.length() <= high.length()) {
            if ((s.length() == low.length() && s.compareTo(low) < 0) || (s.length() == high.length() && s.compareTo(high) > 0)) {
                // do nothing
            } else {
                if (!(s.length() > 1 && s.charAt(0) == '0'))
                    count++;
            }
        }
        if (s.length() + 2 > high.length()) return;
        for (char[] p : pairs) {
            dfs(p[0] + s + p[1], length, low, high);
        }
    }
}
      
var strobogrammaticInRange = function(low, high) {
    let count = 0;
    const pairs = [['0','0'], ['1','1'], ['6','9'], ['8','8'], ['9','6']];
    function dfs(s, length) {
        if (s.length > high.length || (s.length > 1 && s[0] === '0')) return;
        if (s.length >= low.length && s.length <= high.length) {
            if ((s.length === low.length && s < low) || (s.length === high.length && s > high)) {
                // do nothing
            } else {
                if (!(s.length > 1 && s[0] === '0')) count++;
            }
        }
        if (s.length + 2 > high.length) return;
        for (const [a, b] of pairs) {
            dfs(a + s + b, length);
        }
    }
    for (let length = low.length; length <= high.length; length++) {
        if (length === 1) {
            for (const c of ['0','1','8']) {
                if ((low.length === 1 && c < low) || (high.length === 1 && c > high)) continue;
                count++;
            }
        } else {
            for (const [a, b] of pairs) {
                if (a === '0') continue;
                dfs(a + b, length);
            }
        }
    }
    return count;
};
      

Problem Description

The Leetcode problem "Strobogrammatic Number III" asks you to count how many numbers in a given range are strobogrammatic. A strobogrammatic number is a number that looks the same when rotated 180 degrees (turned upside down). For example, 69 becomes 96, 88 remains 88, and 818 remains 818 after rotation.

You are given two strings low and high that represent the lower and upper bounds of the range (inclusive). Your task is to find how many strobogrammatic numbers exist in this range.

  • Both low and high are non-negative integers represented as strings, and may be very large (up to 15 digits).
  • Leading zeros are not allowed, except for the number 0 itself.
  • Return the count of strobogrammatic numbers in the inclusive range [low, high].

Thought Process

To solve this problem, let's first understand what a strobogrammatic number is. Only certain digits are valid in a strobogrammatic number: 0, 1, 6, 8, 9. When flipped, these digits either remain the same or become another valid digit (e.g., 6 becomes 9 and vice versa).

A brute-force approach would be to check every number between low and high and determine if each is strobogrammatic. However, since the range can be very large (up to 1015), this is not feasible.

Instead, we can generate all strobogrammatic numbers whose lengths are between the lengths of low and high, and count only those that fall within the given range. This avoids unnecessary checks and leverages the special structure of strobogrammatic numbers.

Solution Approach

The core idea is to use recursive generation (depth-first search) to construct all strobogrammatic numbers of valid lengths, and count only those within the specified range.

  • Strobogrammatic Pairs: The strobogrammatic digit pairs are: (0,0), (1,1), (6,9), (8,8), and (9,6). These pairs can be "wrapped" around the current string to expand it from the middle outwards.
  • Recursive Construction:
    • Start with an empty string or a single valid center (for odd lengths: '0', '1', '8').
    • At each step, add a pair to the front and back of the current string.
    • Continue until the string reaches the desired length.
  • Range Checking:
    • For each generated number, check if it is within the bounds [low, high].
    • Skip numbers with leading zeros (unless the number is exactly "0").
  • Iterate Over Lengths: Generate strobogrammatic numbers for all lengths from len(low) to len(high), inclusive.
  • Efficiency: This approach avoids generating all numbers in the range and directly builds only valid candidates, making it much faster.

Example Walkthrough

Example: low = "50", high = "100"

  1. Lengths: Both bounds have 2 or 3 digits. We generate strobogrammatic numbers of length 2 and 3.
  2. Length 2:
    • Possible pairs: "11", "69", "88", "96"
    • "00" is not valid because leading zeros are not allowed except for "0" itself.
    • Check which are in [50, 100]: "69", "88", "96" (all valid)
  3. Length 3:
    • Possible centers: "0", "1", "8"
    • Wrap pairs around these centers to build numbers like "101", "609", "808", "906", etc.
    • Check if any 3-digit strobogrammatic numbers fall in [50, 100]: None, since 3-digit numbers start from 100.
  4. Result: The answer is 3 ("69", "88", "96").

Time and Space Complexity

Brute-force Approach: Checking every number between low and high would take O(N) time, where N is the size of the range, which is infeasible for large ranges (up to 1015).

Optimized Approach: The recursive method generates only strobogrammatic numbers. For each possible length, the number of candidates is O(5n/2), where n is the number length (since there are 5 pairs). For each candidate, comparison to bounds is O(n), but the total number of candidates is much smaller than the size of the range.

Space Complexity: The space is mainly used by the recursion stack, which is O(n) for the maximum length, and by storing temporary strings.

Summary

The key insight for "Strobogrammatic Number III" is to avoid brute-force enumeration by generating only valid strobogrammatic numbers of relevant lengths, and counting those within the range. We use recursion to build numbers from the inside out, leveraging the symmetry of strobogrammatic pairs. This approach is both efficient and elegant, allowing us to handle very large ranges without performance issues.