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 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

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

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]))
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?


Recommended Readings

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

Load More