906. Super Palindromes
Problem Description
A super-palindrome is defined as a positive integer that satisfies two conditions:
- It is a palindrome (reads the same forwards and backwards)
- 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 palindrome9 = 3²
, and3
is also a palindrome
-
484
is a super-palindrome because:484
is a palindrome484 = 22²
, and22
is also a palindrome
The task is to find all such numbers within the given range and return the count.
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:
- Even-length palindrome: Mirror the entire number. For example, from
123
we get123321
- Odd-length palindrome: Mirror all but the last digit. For example, from
123
we get12321
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:
- Calculate
p²
- Check if
p²
is within the range[left, right]
- Check if
p²
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
:
- Calculate
x = p²
usingmap(lambda x: x * x, ps)
- Check if
x
is within the range[l, r]
- Check if
x
itself is a palindrome usingis_palindrome(x)
- 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 EvaluatorExample 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 → skip2² = 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 → skip100² = 10000
: Is 10000 in[4, 1000]
? No → skip111² = 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
andright
strings to integers:O(1)
(assuming fixed-length input) - Iterating through all palindromes in
ps
(which has2 * 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)
wherex
can be up to(10^10)^2 = 10^20
, soO(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 stores2 * 10^5
palindrome numbers:O(n)
- The
is_palindrome
function usesO(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
orBigInteger
- 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]))
How does quick sort divide the problem into subproblems?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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!