Facebook Pixel

906. Super Palindromes

HardMathStringEnumeration
Leetcode Link

Problem Description

A super-palindrome is defined as a positive integer that satisfies two conditions:

  1. It is a palindrome (reads the same forwards and backwards)
  2. It is the square of a palindrome

Given two positive integers left and right (represented as strings), you need to count how many super-palindromes exist in the inclusive range [left, right].

For example:

  • 9 is a super-palindrome because:

    • 9 is a palindrome
    • 9 = 3², and 3 is also a palindrome
  • 484 is a super-palindrome because:

    • 484 is a palindrome
    • 484 = 22², and 22 is also a palindrome

The task is to find all such numbers within the given range and return the count.

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

Intuition

The key insight is recognizing that checking every number in the range [left, right] would be inefficient, especially since the range can be as large as 10^18. Instead, we should work backwards from the definition of super-palindromes.

Since a super-palindrome must be the square of a palindrome, we can generate palindromes first, square them, and then check if the result is also a palindrome and falls within our range. This dramatically reduces the search space.

Given that the maximum value is around 10^18, the maximum palindrome whose square could be a super-palindrome is approximately √(10^18) = 10^9. However, we need to generate palindromes efficiently.

To generate palindromes systematically, we can use a clever technique: take the first half of the palindrome and mirror it. For any number i, we can create two types of palindromes:

  1. Even-length palindrome: Mirror the entire number. For example, from 123 we get 123321
  2. Odd-length palindrome: Mirror all but the last digit. For example, from 123 we get 12321

Since we're looking for palindromes up to 10^9, and palindromes are generated by mirroring their first half, we only need to iterate through numbers up to about 10^5 (since mirroring a 5-digit number gives us up to a 10-digit palindrome).

The preprocessing step generates all possible palindromes by iterating through i from 1 to 10^5, creating both even and odd-length palindromes from each i. Then, for each palindrome p in our precomputed list, we:

  1. Calculate
  2. Check if is within the range [left, right]
  3. Check if is itself a palindrome

This approach is much more efficient than brute force because we're only checking the squares of palindromes rather than all numbers in the potentially massive range.

Learn more about Math patterns.

Solution Approach

The solution consists of two main parts: preprocessing palindromes and checking for super-palindromes.

Step 1: Generate All Palindromes (Preprocessing)

We iterate through numbers from 1 to 10^5 and generate palindromes:

for i in range(1, 10**5 + 1):
    s = str(i)
    t1 = s[::-1]  # Reverse of the entire string
    t2 = s[:-1][::-1]  # Reverse of string without last character
    ps.append(int(s + t1))  # Even-length palindrome
    ps.append(int(s + t2))  # Odd-length palindrome

For example, when i = 123:

  • s + t1 creates "123" + "321" = "123321" (even-length)
  • s + t2 creates "123" + "21" = "12321" (odd-length)

This generates all palindromes up to approximately 10^10, which when squared can reach up to 10^20, covering our required range.

Step 2: Check if a Number is a Palindrome

The helper function efficiently checks palindrome without string conversion:

def is_palindrome(x: int) -> bool:
    y, t = 0, x
    while t:
        y = y * 10 + t % 10
        t //= 10
    return x == y

This function reverses the number mathematically:

  • Extract the last digit using t % 10
  • Build the reversed number by multiplying y by 10 and adding the digit
  • Remove the last digit from t using integer division

Step 3: Count Super-Palindromes

l, r = int(left), int(right)
return sum(l <= x <= r and is_palindrome(x) for x in map(lambda x: x * x, ps))

For each palindrome p in our precomputed list ps:

  1. Calculate x = p² using map(lambda x: x * x, ps)
  2. Check if x is within the range [l, r]
  3. Check if x itself is a palindrome using is_palindrome(x)
  4. Count all numbers satisfying both conditions

Time Complexity: O(M^(1/4) × log M) where M is the upper bound (10^18). We generate O(M^(1/4)) palindromes, and for each palindrome's square, we check if it's a palindrome in O(log M) time.

Space Complexity: O(M^(1/4)) to store all the generated palindromes in the list ps.

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 super-palindromes in the range [4, 1000].

Step 1: Generate palindromes

Starting with small values of i:

When i = 1:

  • Even-length: "1" + "1" = "11" → palindrome = 11
  • Odd-length: "1" + "" = "1" → palindrome = 1

When i = 2:

  • Even-length: "2" + "2" = "22" → palindrome = 22
  • Odd-length: "2" + "" = "2" → palindrome = 2

When i = 3:

  • Even-length: "3" + "3" = "33" → palindrome = 33
  • Odd-length: "3" + "" = "3" → palindrome = 3

When i = 10:

  • Even-length: "10" + "01" = "1001" → palindrome = 1001
  • Odd-length: "10" + "0" = "100" → palindrome = 100 (not a palindrome, but we still include it)

When i = 11:

  • Even-length: "11" + "11" = "1111" → palindrome = 1111
  • Odd-length: "11" + "1" = "111" → palindrome = 111

We continue this process up to i = 10^5, building our list ps = [1, 1, 11, 2, 22, 2, 33, 3, ...]

Step 2: Check each palindrome's square

For each palindrome in ps, we square it and check:

  • 1² = 1: Is 1 in [4, 1000]? No → skip
  • 2² = 4: Is 4 in [4, 1000]? Yes → Is 4 a palindrome? Yes → Count it!
  • 3² = 9: Is 9 in [4, 1000]? Yes → Is 9 a palindrome? Yes → Count it!
  • 11² = 121: Is 121 in [4, 1000]? Yes → Is 121 a palindrome? Yes → Count it!
  • 22² = 484: Is 484 in [4, 1000]? Yes → Is 484 a palindrome? Yes → Count it!
  • 33² = 1089: Is 1089 in [4, 1000]? No → skip
  • 100² = 10000: Is 10000 in [4, 1000]? No → skip
  • 111² = 12321: Is 12321 in [4, 1000]? No → skip

Step 3: Verify palindrome check for 484

Using the is_palindrome function on 484:

  • Initialize: y = 0, t = 484
  • Iteration 1: y = 0 * 10 + 4 = 4, t = 48
  • Iteration 2: y = 4 * 10 + 8 = 48, t = 4
  • Iteration 3: y = 48 * 10 + 4 = 484, t = 0
  • Compare: 484 == 484 → True, it's a palindrome!

Result: In the range [4, 1000], we found 4 super-palindromes: 4, 9, 121, 484

Solution Implementation

1# Pre-generate all possible palindrome roots up to 10^5
2# Since we're looking for palindromes whose squares are also palindromes,
3# we only need to check palindrome numbers as potential roots
4palindrome_roots = []
5for num in range(1, 10**5 + 1):
6    num_str = str(num)
7  
8    # Create odd-length palindrome: "123" -> "123321"
9    reversed_str = num_str[::-1]
10    odd_palindrome = int(num_str + reversed_str)
11    palindrome_roots.append(odd_palindrome)
12  
13    # Create even-length palindrome: "123" -> "12321" (excluding last digit before reversing)
14    reversed_without_last = num_str[:-1][::-1]
15    even_palindrome = int(num_str + reversed_without_last)
16    palindrome_roots.append(even_palindrome)
17
18
19class Solution:
20    def superpalindromesInRange(self, left: str, right: str) -> int:
21        """
22        Count super palindromes in the given range [left, right].
23        A super palindrome is a number that is a palindrome and its square root is also a palindrome.
24      
25        Args:
26            left: Lower bound as string
27            right: Upper bound as string
28          
29        Returns:
30            Count of super palindromes in the range
31        """
32      
33        def is_palindrome(number: int) -> bool:
34            """
35            Check if a number is a palindrome by reversing its digits.
36          
37            Args:
38                number: Integer to check
39              
40            Returns:
41                True if the number is a palindrome, False otherwise
42            """
43            reversed_number = 0
44            temp = number
45          
46            # Reverse the digits of the number
47            while temp > 0:
48                reversed_number = reversed_number * 10 + temp % 10
49                temp //= 10
50              
51            return number == reversed_number
52      
53        # Convert string bounds to integers
54        left_bound = int(left)
55        right_bound = int(right)
56      
57        # Count super palindromes: check if square of each palindrome root
58        # is within range and is also a palindrome
59        count = 0
60        for root in palindrome_roots:
61            square = root * root
62            if left_bound <= square <= right_bound and is_palindrome(square):
63                count += 1
64              
65        return count
66
1class Solution {
2    // Pre-computed array of palindromic numbers
3    private static long[] palindromes;
4
5    // Static initialization block to generate all palindromes
6    static {
7        // Array to store 200,000 palindromes (2 * 100,000)
8        palindromes = new long[2 * (int) 1e5];
9      
10        // Generate palindromes by creating numbers and their mirror reflections
11        for (int i = 1; i <= 1e5; i++) {
12            // Convert number to string for manipulation
13            String numberStr = Integer.toString(i);
14          
15            // Create even-length palindrome by appending reversed string
16            String reversedStr = new StringBuilder(numberStr).reverse().toString();
17            palindromes[2 * i - 2] = Long.parseLong(numberStr + reversedStr);
18          
19            // Create odd-length palindrome by removing last digit before reversing
20            String truncatedReversed = new StringBuilder(numberStr.substring(0, numberStr.length() - 1))
21                                           .reverse()
22                                           .toString();
23            palindromes[2 * i - 1] = Long.parseLong(numberStr + truncatedReversed);
24        }
25    }
26
27    /**
28     * Counts super-palindromes within the given range [left, right]
29     * A super-palindrome is a number that is a palindrome and its square root is also a palindrome
30     * 
31     * @param left  The lower bound of the range (inclusive) as a string
32     * @param right The upper bound of the range (inclusive) as a string
33     * @return The count of super-palindromes in the range
34     */
35    public int superpalindromesInRange(String left, String right) {
36        // Convert string boundaries to long values
37        long leftBound = Long.parseLong(left);
38        long rightBound = Long.parseLong(right);
39      
40        // Counter for super-palindromes found
41        int count = 0;
42      
43        // Check each pre-computed palindrome
44        for (long palindrome : palindromes) {
45            // Calculate the square of the palindrome
46            long square = palindrome * palindrome;
47          
48            // Check if square is within range and is itself a palindrome
49            if (square >= leftBound && square <= rightBound && isPalindrome(square)) {
50                count++;
51            }
52        }
53      
54        return count;
55    }
56
57    /**
58     * Checks if a given number is a palindrome
59     * 
60     * @param number The number to check
61     * @return true if the number is a palindrome, false otherwise
62     */
63    private boolean isPalindrome(long number) {
64        // Build the reversed number
65        long reversedNumber = 0;
66        long temp = number;
67      
68        // Reverse the digits of the number
69        while (temp > 0) {
70            reversedNumber = reversedNumber * 10 + temp % 10;
71            temp /= 10;
72        }
73      
74        // A number is a palindrome if it equals its reverse
75        return number == reversedNumber;
76    }
77}
78
1using ll = unsigned long long;
2
3// Pre-computed array to store all palindromes that can be formed from numbers 1 to 100000
4// Size is 2 * 100000 because each number generates 2 palindromes (odd and even length)
5ll palindromes[2 * 100000];
6
7// Static initialization block that runs before main()
8int init = [] {
9    // Generate palindromes from each number i
10    for (int i = 1; i <= 100000; i++) {
11        // Convert number to string
12        string numStr = to_string(i);
13      
14        // Create odd-length palindrome by appending reversed string to original
15        // Example: 123 -> 123321
16        string reversedStr = numStr;
17        reverse(reversedStr.begin(), reversedStr.end());
18        palindromes[2 * i - 2] = stoll(numStr + reversedStr);
19      
20        // Create even-length palindrome by removing last digit before reversing
21        // Example: 123 -> 12321
22        string truncatedStr = numStr.substr(0, numStr.length() - 1);
23        reverse(truncatedStr.begin(), truncatedStr.end());
24        palindromes[2 * i - 1] = stoll(numStr + truncatedStr);
25    }
26    return 0;
27}();
28
29class Solution {
30public:
31    /**
32     * Counts super-palindromes in the given range
33     * A super-palindrome is a number that is a palindrome and its square root is also a palindrome
34     * @param left - Lower bound of the range (inclusive)
35     * @param right - Upper bound of the range (inclusive)
36     * @return Number of super-palindromes in the range
37     */
38    int superpalindromesInRange(string left, string right) {
39        // Convert string boundaries to unsigned long long
40        ll leftBound = stoll(left);
41        ll rightBound = stoll(right);
42      
43        int count = 0;
44      
45        // Check each pre-computed palindrome
46        for (ll palindrome : palindromes) {
47            // Calculate the square of the palindrome
48            ll square = palindrome * palindrome;
49          
50            // Check if the square is within range and is also a palindrome
51            if (square >= leftBound && square <= rightBound && isPalindrome(square)) {
52                ++count;
53            }
54        }
55      
56        return count;
57    }
58  
59private:
60    /**
61     * Checks if a number is a palindrome
62     * @param num - The number to check
63     * @return true if the number is a palindrome, false otherwise
64     */
65    bool isPalindrome(ll num) {
66        ll reversed = 0;
67        ll temp = num;
68      
69        // Reverse the number by extracting digits from right to left
70        while (temp > 0) {
71            reversed = reversed * 10 + temp % 10;
72            temp /= 10;
73        }
74      
75        // A number is a palindrome if it equals its reverse
76        return num == reversed;
77    }
78};
79
1// Array to store all palindromes that will be used to generate super palindromes
2const palindromes: number[] = Array(200000).fill(0);
3
4// Initialize the palindromes array with all possible palindrome bases
5const initializePalindromes = (() => {
6    // Generate palindromes by taking numbers from 1 to 100000
7    for (let i = 1; i <= 100000; ++i) {
8        const numberStr: string = i.toString();
9      
10        // Create even-length palindrome by mirroring the entire number
11        // Example: 123 -> 123321
12        const evenPalindrome: string = numberStr + numberStr.split('').reverse().join('');
13      
14        // Create odd-length palindrome by mirroring all but the last digit
15        // Example: 123 -> 12321
16        const oddPalindrome: string = numberStr + numberStr.slice(0, -1).split('').reverse().join('');
17      
18        // Store both palindromes in the array
19        palindromes[2 * i - 2] = parseInt(evenPalindrome, 10);
20        palindromes[2 * i - 1] = parseInt(oddPalindrome, 10);
21    }
22})();
23
24/**
25 * Finds the number of super palindromes in the given range [left, right]
26 * A super palindrome is a number that is a palindrome and its square root is also a palindrome
27 * @param left - The lower bound of the range (inclusive) as a string
28 * @param right - The upper bound of the range (inclusive) as a string
29 * @returns The count of super palindromes in the range
30 */
31function superpalindromesInRange(left: string, right: string): number {
32    // Convert string bounds to BigInt for handling large numbers
33    const lowerBound: bigint = BigInt(left);
34    const upperBound: bigint = BigInt(right);
35  
36    /**
37     * Helper function to check if a number is a palindrome
38     * @param num - The number to check
39     * @returns True if the number is a palindrome, false otherwise
40     */
41    const isPalindrome = (num: bigint): boolean => {
42        const numStr: string = num.toString();
43        return numStr === numStr.split('').reverse().join('');
44    };
45  
46    // Counter for super palindromes found in the range
47    let superPalindromeCount: number = 0;
48  
49    // Check each palindrome base to see if its square is a super palindrome
50    for (const palindromeBase of palindromes) {
51        // Calculate the square of the palindrome base
52        const squaredValue: bigint = BigInt(palindromeBase) * BigInt(palindromeBase);
53      
54        // Check if the squared value is within range and is itself a palindrome
55        if (squaredValue >= lowerBound && squaredValue <= upperBound && isPalindrome(squaredValue)) {
56            ++superPalindromeCount;
57        }
58    }
59  
60    return superPalindromeCount;
61}
62

Time and Space Complexity

Time Complexity: O(n) where n = 10^5

The preprocessing step iterates through numbers from 1 to 10^5, and for each number:

  • String conversion takes O(log i) time
  • String reversal takes O(log i) time
  • Creating palindromes takes O(log i) time
  • Total preprocessing: O(n * log n)

For the main function:

  • Converting left and right strings to integers: O(1) (assuming fixed-length input)
  • Iterating through all palindromes in ps (which has 2 * 10^5 elements): O(n)
  • For each palindrome, computing x * x: O(1)
  • Checking if x is in range: O(1)
  • is_palindrome function: O(log x) where x can be up to (10^10)^2 = 10^20, so O(20) = O(1)
  • Total main function: O(n)

Overall time complexity: O(n * log n) for preprocessing + O(n) for query = O(n * log n)

Since preprocessing happens once and n = 10^5 is constant, this simplifies to O(1) amortized per query.

Space Complexity: O(n) where n = 10^5

  • The ps list stores 2 * 10^5 palindrome numbers: O(n)
  • The is_palindrome function uses O(1) auxiliary space
  • Other variables use O(1) space

Total space complexity: O(n) = O(10^5) = O(1) since it's a fixed constant.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow When Squaring Large Palindromes

The most critical pitfall in this solution is potential integer overflow. When generating palindromes up to 10^5, the resulting palindromes can be as large as 10^10. When squared, these values can reach 10^20, which exceeds the maximum value for standard 32-bit integers and even approaches the limits of 64-bit integers in some languages.

Problem Example:

# A palindrome like 9999999999 (10 digits)
# When squared: 9999999999^2 = 99999999980000000001 (20 digits)
# This could cause overflow in languages with fixed integer sizes

Solution:

  • In Python, this isn't an issue since integers have arbitrary precision
  • In other languages like Java or C++, use long long or BigInteger
  • Add boundary checks before squaring to prevent overflow

2. Inefficient Palindrome Generation Bounds

Generating palindromes up to 10^5 creates many unnecessary palindromes whose squares will far exceed the typical input range. This wastes memory and computation time.

Problem Example:

# If right = "1000000000" (10^9), we only need palindromes up to sqrt(10^9) ≈ 31623
# But we're generating palindromes up to 10^10, most of which are unnecessary

Solution:

def superpalindromesInRange(self, left: str, right: str) -> int:
    right_bound = int(right)
    # Calculate the maximum palindrome we need to check
    max_palindrome_needed = int(right_bound ** 0.5) + 1
  
    # Generate palindromes only up to what's needed
    palindrome_roots = []
    limit = min(10**5, max_palindrome_needed)
    for num in range(1, limit + 1):
        # ... rest of palindrome generation

3. Duplicate Palindromes in the List

The current generation method can create duplicate palindromes, especially for small numbers.

Problem Example:

# When num = 1:
# odd_palindrome: "1" + "1" = "11"
# even_palindrome: "1" + "" = "1"
# When num = 11:
# odd_palindrome: "11" + "11" = "1111"
# even_palindrome: "11" + "1" = "111"
# But "111" might be generated again from num = 111

Solution:

# Use a set to store palindromes and avoid duplicates
palindrome_roots = set()
for num in range(1, 10**5 + 1):
    num_str = str(num)
    palindrome_roots.add(int(num_str + num_str[::-1]))
    palindrome_roots.add(int(num_str + num_str[:-1][::-1]))
palindrome_roots = sorted(list(palindrome_roots))

4. Edge Case: Single-Digit Numbers

The palindrome generation might miss single-digit numbers (1-9), which are palindromes themselves and some of which have palindromic squares.

Problem Example:

# Numbers 1, 4, 9 are super-palindromes (1^2=1, 2^2=4, 3^2=9)
# The current generation starts from num=1 but creates "11" and "1"
# It might miss handling these edge cases properly

Solution:

# Explicitly include single-digit palindromes
palindrome_roots = list(range(1, 10))  # Start with 1-9
for num in range(1, 10**5 + 1):
    # ... continue with regular generation

5. String Slicing Index Error

When creating even-length palindromes with num_str[:-1][::-1], if num_str has only one character, num_str[:-1] becomes an empty string.

Problem Example:

# When num = 1 through 9:
# num_str = "1", num_str[:-1] = "" (empty string)
# This creates the same palindrome twice: "1" + "" = "1"

Solution:

for num in range(1, 10**5 + 1):
    num_str = str(num)
    palindrome_roots.append(int(num_str + num_str[::-1]))
    if len(num_str) > 1:  # Only create even-length if meaningful
        palindrome_roots.append(int(num_str + num_str[:-1][::-1]))
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does quick sort divide the problem into subproblems?


Recommended Readings

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

Load More