Facebook Pixel

2217. Find Palindrome With Fixed Length

Problem Description

You are given an array of integers called queries and a positive integer intLength. Your task is to find specific palindromes based on these inputs.

For each value queries[i], you need to find the queries[i]-th smallest positive palindrome that has exactly intLength digits.

A palindrome is a number that reads the same forwards and backwards. For example, 121 and 1331 are palindromes. Important constraints:

  • Palindromes cannot have leading zeros (so 010 is not valid)
  • The palindromes must have exactly intLength digits

Your output should be an array answer where:

  • answer[i] contains the queries[i]-th smallest palindrome of length intLength
  • If the queries[i]-th palindrome doesn't exist (because there aren't that many palindromes of the given length), then answer[i] should be -1

For example, if intLength = 3, the smallest positive palindromes would be: 101, 111, 121, 131, 141, etc. If queries = [1, 2, 5], then:

  • The 1st smallest 3-digit palindrome is 101
  • The 2nd smallest 3-digit palindrome is 111
  • The 5th smallest 3-digit palindrome is 141
  • So the answer would be [101, 111, 141]

The solution leverages the fact that for a palindrome, you only need to determine the first half of the digits (rounding up), and the second half mirrors the first half. This significantly reduces the search space.

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

Intuition

The key insight is that we don't need to generate all palindromes to find the k-th one. Instead, we can directly compute it by understanding the structure of palindromes.

For a palindrome of length intLength, we only need to determine the first half of the digits (including the middle digit if the length is odd). The second half is just a mirror of the first half. This observation drastically simplifies our problem.

Consider palindromes of different lengths:

  • Length 3: 101, 111, 121, ... (determined by first 2 digits: 10, 11, 12, ...)
  • Length 4: 1001, 1111, 1221, ... (determined by first 2 digits: 10, 11, 12, ...)
  • Length 5: 10001, 10101, 10201, ... (determined by first 3 digits: 100, 101, 102, ...)

We notice that:

  1. The number of digits we need to determine is (intLength + 1) // 2
  2. These determining digits form a sequence starting from 10^(l-1) where l is the number of digits to determine
  3. The k-th palindrome corresponds to the k-th number in this sequence

For example, with intLength = 5:

  • We need to determine l = (5 + 1) // 2 = 3 digits
  • The sequence starts at 100 (which gives palindrome 10001)
  • The 2nd palindrome comes from 101 (giving 10101)
  • The 3rd palindrome comes from 102 (giving 10201)

To construct the full palindrome from the first half:

  • Take the first half digits
  • For the second half, reverse the first half
  • If intLength is odd, skip the middle digit when reversing (since it's already included)

The range of valid first-half values is from 10^(l-1) to 10^l - 1. If our query asks for a palindrome beyond this range, we return -1.

Learn more about Math patterns.

Solution Approach

Let's walk through the implementation step by step:

  1. Calculate the length of the first half:

    l = (intLength + 1) >> 1

    This uses right shift (>> 1) which is equivalent to dividing by 2. For a palindrome of length intLength, we need l digits to determine the entire palindrome.

  2. Determine the valid range for the first half:

    start, end = 10 ** (l - 1), 10**l - 1
    • start: The smallest l-digit number (e.g., 100 for 3 digits)
    • end: The largest l-digit number (e.g., 999 for 3 digits)

    These bounds ensure we don't have leading zeros and stay within the valid range.

  3. Process each query:

    for q in queries:
        v = start + q - 1

    For the q-th palindrome, we calculate the corresponding first half value v. Since start is the 1st palindrome's first half, we add q - 1 to get the q-th one.

  4. Check validity:

    if v > end:
        ans.append(-1)
        continue

    If v exceeds our upper bound, the q-th palindrome doesn't exist for this length.

  5. Construct the palindrome:

    s = str(v)
    s += s[::-1][intLength % 2 :]
    • Convert the first half v to string
    • Reverse the string with s[::-1]
    • If intLength is odd (intLength % 2 = 1), skip the first character of the reversed string (the middle digit) using slice [1:]
    • If intLength is even (intLength % 2 = 0), use the entire reversed string with slice [0:]
    • Concatenate to form the complete palindrome
  6. Convert and store result:

    ans.append(int(s))

    Convert the palindrome string back to integer and add to results.

Example walkthrough for intLength = 5, queries = [2]:

  • l = (5 + 1) >> 1 = 3
  • start = 100, end = 999
  • For query 2: v = 100 + 2 - 1 = 101
  • s = "101"
  • Reverse and skip middle: "101"[::-1][1:] = "01"
  • Final palindrome: "101" + "01" = "10101"
  • Result: 10101

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 a concrete example with intLength = 4 and queries = [1, 3, 7].

Step 1: Determine the first half length

  • Since we need 4-digit palindromes, we calculate: l = (4 + 1) // 2 = 2
  • We only need to determine the first 2 digits

Step 2: Find the valid range

  • start = 10^(2-1) = 10 (smallest 2-digit number)
  • end = 10^2 - 1 = 99 (largest 2-digit number)
  • So our first-half values can range from 10 to 99

Step 3: Process each query

Query 1 (find 1st palindrome):

  • Calculate first half: v = 10 + 1 - 1 = 10
  • Check validity: 10 ≤ 99
  • Construct palindrome:
    • First half: "10"
    • Reverse: "01"
    • Since intLength is even (4 % 2 = 0), use entire reversed string
    • Result: "10" + "01" = "1001"
  • Answer: 1001

Query 3 (find 3rd palindrome):

  • Calculate first half: v = 10 + 3 - 1 = 12
  • Check validity: 12 ≤ 99
  • Construct palindrome:
    • First half: "12"
    • Reverse: "21"
    • Use entire reversed string (even length)
    • Result: "12" + "21" = "1221"
  • Answer: 1221

Query 7 (find 7th palindrome):

  • Calculate first half: v = 10 + 7 - 1 = 16
  • Check validity: 16 ≤ 99
  • Construct palindrome:
    • First half: "16"
    • Reverse: "61"
    • Result: "16" + "61" = "1661"
  • Answer: 1661

Final Result: [1001, 1221, 1661]

This demonstrates how we can directly compute the k-th palindrome without generating all preceding ones. The pattern becomes clear: for 4-digit palindromes, we have:

  • 1st: 10→1001
  • 2nd: 11→1111
  • 3rd: 12→1221
  • ...and so on

For an odd-length example with intLength = 3 and queries = [2]:

  • l = (3 + 1) // 2 = 2
  • First half value: v = 10 + 2 - 1 = 11
  • Construct: "11" + "11"[::-1][1:] = "11" + "1" = "111"
  • The middle digit (second '1') is shared, not duplicated

Solution Implementation

1class Solution:
2    def kthPalindrome(self, queries: List[int], intLength: int) -> List[int]:
3        # Calculate the length of the first half of the palindrome
4        # For odd length palindromes, we include the middle digit in the first half
5        half_length = (intLength + 1) >> 1  # Equivalent to (intLength + 1) // 2
6      
7        # Define the range of possible values for the first half
8        # The smallest first half starts with 1 (e.g., 10, 100, 1000...)
9        min_half = 10 ** (half_length - 1)
10        # The largest first half is all 9s (e.g., 99, 999, 9999...)
11        max_half = 10 ** half_length - 1
12      
13        result = []
14      
15        for query in queries:
16            # Calculate the first half value for the k-th palindrome
17            # query is 1-indexed, so we add (query - 1) to min_half
18            first_half_value = min_half + query - 1
19          
20            # Check if the requested palindrome exists
21            if first_half_value > max_half:
22                result.append(-1)
23                continue
24          
25            # Convert the first half to string for manipulation
26            first_half_str = str(first_half_value)
27          
28            # Build the palindrome by appending the reversed first half
29            # For odd length palindromes, skip the middle digit when reversing
30            # intLength % 2 gives 1 for odd lengths, 0 for even lengths
31            palindrome_str = first_half_str + first_half_str[::-1][intLength % 2:]
32          
33            # Convert back to integer and add to result
34            result.append(int(palindrome_str))
35      
36        return result
37
1class Solution {
2    public long[] kthPalindrome(int[] queries, int intLength) {
3        int queryCount = queries.length;
4        long[] result = new long[queryCount];
5      
6        // Calculate the length of the first half of the palindrome
7        // For odd length palindromes, we include the middle digit
8        int halfLength = (intLength + 1) >> 1;  // Equivalent to (intLength + 1) / 2
9      
10        // Calculate the range of valid first halves
11        long minHalf = (long) Math.pow(10, halfLength - 1);  // Smallest n-digit number
12        long maxHalf = (long) Math.pow(10, halfLength) - 1;  // Largest n-digit number
13      
14        // Process each query
15        for (int i = 0; i < queryCount; ++i) {
16            // Calculate the first half value for the k-th palindrome
17            // queries[i] is 1-indexed, so we subtract 1
18            long firstHalf = minHalf + queries[i] - 1;
19          
20            // Check if the requested palindrome exists
21            if (firstHalf > maxHalf) {
22                result[i] = -1;
23                continue;
24            }
25          
26            // Convert the first half to string
27            String halfString = "" + firstHalf;
28          
29            // Build the complete palindrome by appending the reversed half
30            // For odd length palindromes, skip the middle digit when reversing
31            String reversedPart = new StringBuilder(halfString)
32                .reverse()
33                .substring(intLength % 2);  // Skip first char if odd length
34          
35            // Combine both parts to form the complete palindrome
36            String palindrome = halfString + reversedPart;
37          
38            // Convert the palindrome string to long and store in result
39            result[i] = Long.parseLong(palindrome);
40        }
41      
42        return result;
43    }
44}
45
1class Solution {
2public:
3    vector<long long> kthPalindrome(vector<int>& queries, int intLength) {
4        // Calculate the length of the first half of the palindrome
5        // For odd length palindromes, we include the middle digit in the first half
6        int halfLength = (intLength + 1) >> 1;  // Equivalent to (intLength + 1) / 2
7      
8        // Calculate the range of possible values for the first half
9        // e.g., for halfLength = 3: start = 100, end = 999
10        long long startValue = pow(10, halfLength - 1);
11        long long endValue = pow(10, halfLength) - 1;
12      
13        vector<long long> result;
14      
15        // Process each query
16        for (int& query : queries) {
17            // Calculate the value of the first half for the k-th palindrome
18            // query is 1-indexed, so we subtract 1 to get 0-indexed position
19            long long firstHalfValue = startValue + query - 1;
20          
21            // Check if the requested palindrome exists
22            if (firstHalfValue > endValue) {
23                result.push_back(-1);
24                continue;
25            }
26          
27            // Convert the first half to string for manipulation
28            string firstHalf = to_string(firstHalfValue);
29          
30            // Create a copy for reversing
31            string reversedHalf = firstHalf;
32            reverse(reversedHalf.begin(), reversedHalf.end());
33          
34            // Build the complete palindrome
35            // For odd length: skip the middle digit when appending (already in firstHalf)
36            // For even length: append the entire reversed string
37            firstHalf += reversedHalf.substr(intLength % 2);
38          
39            // Convert the palindrome string back to long long and add to result
40            result.push_back(stoll(firstHalf));
41        }
42      
43        return result;
44    }
45};
46
1/**
2 * Finds the k-th palindrome number with a specific length for each query
3 * @param queries - Array of k values representing the k-th palindrome to find
4 * @param intLength - The desired length of palindrome numbers
5 * @returns Array of palindrome numbers corresponding to each query, or -1 if not exists
6 */
7function kthPalindrome(queries: number[], intLength: number): number[] {
8    // Determine if the palindrome length is odd
9    const isOddLength = intLength % 2 === 1;
10  
11    // Calculate the number of digits in the first half of the palindrome
12    // For odd length, we include the middle digit in the first half
13    const halfLength = Math.floor(intLength / 2) + (isOddLength ? 1 : 0);
14  
15    // Calculate the smallest number that forms the first half of palindrome
16    // e.g., for length 4 or 5, starts from 10; for length 6 or 7, starts from 100
17    const firstHalfStart = Math.pow(10, halfLength - 1);
18  
19    // Calculate the maximum number of palindromes possible with this length
20    // The first half can range from firstHalfStart to (firstHalfStart * 10 - 1)
21    const maxPalindromeCount = firstHalfStart * 9;
22  
23    // Process each query to find the corresponding palindrome
24    return queries.map(kthValue => {
25        // Check if the requested k-th palindrome exists
26        if (kthValue > maxPalindromeCount) {
27            return -1;
28        }
29      
30        // Calculate the first half of the k-th palindrome
31        const firstHalf = firstHalfStart + kthValue - 1;
32      
33        // Convert first half to string for manipulation
34        const firstHalfString = firstHalf.toString();
35      
36        // Create the second half by reversing the first half
37        // For odd length palindromes, skip the middle digit when creating the second half
38        const secondHalfString = firstHalfString
39            .split('')
40            .reverse()
41            .slice(isOddLength ? 1 : 0)
42            .join('');
43      
44        // Combine first half and second half to form the complete palindrome
45        const palindrome = Number(firstHalfString + secondHalfString);
46      
47        return palindrome;
48    });
49}
50

Time and Space Complexity

Time Complexity: O(m * l) where m is the length of queries and l is the length of intLength.

  • The algorithm calculates l = (intLength + 1) >> 1, which is O(1).
  • Computing start and end using exponentiation 10 ** (l - 1) and 10**l - 1 takes O(l) time each.
  • The main loop iterates through each query in queries, which runs m times.
  • Inside each iteration:
    • Computing v = start + q - 1 is O(1).
    • Comparison v > end is O(1).
    • Converting v to string takes O(l) time (since v has approximately l digits).
    • String reversal s[::-1] takes O(l) time.
    • String slicing s[::-1][intLength % 2:] takes O(l) time in the worst case.
    • String concatenation takes O(l) time.
    • Converting the final string back to integer takes O(intLength) time.
  • Since each iteration performs O(l) operations and we have m iterations, the total time complexity is O(m * l).

Space Complexity: O(m * intLength)

  • Variables l, start, and end use O(1) space.
  • The ans list stores up to m integers, where each integer can have up to intLength digits, requiring O(m * intLength) space.
  • Within each iteration:
    • String s uses O(l) space.
    • The reversed and sliced string uses O(l) space temporarily.
    • The concatenated palindrome string uses O(intLength) space.
  • The dominant space usage is the ans list containing all results, giving us O(m * intLength) space complexity.

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

Common Pitfalls

1. Off-by-One Error in Query Indexing

A frequent mistake is forgetting that queries are 1-indexed while our calculation is 0-indexed. Developers might write:

# WRONG: Missing the -1 adjustment
first_half_value = min_half + query

This would give us the (k+1)-th palindrome instead of the k-th palindrome.

Solution: Always remember to subtract 1 from the query:

first_half_value = min_half + query - 1

2. Incorrect Handling of Odd vs Even Length Palindromes

When constructing the palindrome, it's easy to confuse when to skip the middle character. A common error:

# WRONG: Using wrong condition for slicing
palindrome_str = first_half_str + first_half_str[::-1][1:]  # Always skips first char
# or
palindrome_str = first_half_str + first_half_str[::-1][(intLength + 1) % 2:]  # Wrong formula

Solution: Use intLength % 2 for the slice start:

  • For odd lengths (e.g., 5): intLength % 2 = 1, skip the middle digit
  • For even lengths (e.g., 4): intLength % 2 = 0, use all digits
palindrome_str = first_half_str + first_half_str[::-1][intLength % 2:]

3. Integer Division vs Bit Shift Confusion

While (intLength + 1) >> 1 and (intLength + 1) // 2 are equivalent for positive integers, using regular division / instead of integer division // causes issues:

# WRONG: Regular division returns float
half_length = (intLength + 1) / 2  # Returns 2.5 for intLength=4
min_half = 10 ** (half_length - 1)  # 10 ** 1.5 gives wrong result

Solution: Use integer division or bit shift:

half_length = (intLength + 1) // 2  # or (intLength + 1) >> 1

4. Boundary Check After Construction

Some might check if the palindrome is valid after constructing it:

# INEFFICIENT: Constructing palindrome before checking validity
first_half_value = min_half + query - 1
palindrome_str = str(first_half_value) + str(first_half_value)[::-1][intLength % 2:]
if len(palindrome_str) > intLength:  # Wrong check
    result.append(-1)

Solution: Check boundaries before construction to avoid unnecessary work:

if first_half_value > max_half:
    result.append(-1)
    continue
# Only construct palindrome if valid

5. Forgetting Edge Cases for Single-Digit Palindromes

When intLength = 1, the logic still works but might be confusing:

  • half_length = 1
  • min_half = 1, max_half = 9
  • The slice [intLength % 2:] becomes [1:] which correctly gives an empty string

However, some might try to handle this as a special case unnecessarily, adding complexity and potential bugs.

Solution: Trust the general formula - it handles all cases including intLength = 1 correctly without special casing.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

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

Load More