Facebook Pixel

1842. Next Palindrome Using Same Digits πŸ”’

Problem Description

You are given a numeric string num that represents a very large palindrome (a number that reads the same forwards and backwards).

Your task is to find the smallest palindrome that is larger than num and can be created by rearranging the digits of num. If no such palindrome exists, return an empty string "".

The key insight is that since the input is already a palindrome, we need to find the next lexicographically larger arrangement of its digits that still forms a palindrome.

The solution works by focusing on just the first half of the palindrome. Since a palindrome is symmetric, once we determine what the first half should be, the second half is automatically determined (it mirrors the first half).

The algorithm:

  1. Extract the first half of the palindrome string
  2. Find the next permutation of this first half (the next lexicographically larger arrangement)
  3. If a next permutation exists, mirror it to create the second half, forming a complete palindrome
  4. If no next permutation exists (meaning the first half is already in descending order), return ""

For example, if num = "1221", the first half is "12". The next permutation of "12" is "21", so the resulting palindrome would be "2112".

The next_permutation function implements the classic algorithm to find the next lexicographically larger permutation by:

  • Finding the rightmost position where a digit can be increased
  • Swapping it with the smallest digit to its right that is larger than it
  • Reversing the remaining digits to get the smallest possible arrangement
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we're dealing with a palindrome, we know that the second half is completely determined by the first half - it's just a mirror image. This means we don't need to consider all the digits independently; we only need to focus on rearranging the first half.

The key realization is that finding the "smallest palindrome larger than num" is equivalent to finding the next lexicographically larger arrangement of the first half. Why? Because once we have the next arrangement of the first half, mirroring it automatically gives us the next palindrome.

Think about it this way: if we have a palindrome like "1221", the first half is "12" and it determines the entire palindrome. To get the next larger palindrome, we need the first half to become "21", which then creates "2112" when mirrored.

But how do we ensure it's the smallest palindrome larger than the original? This is where the "next permutation" algorithm comes in. The next permutation gives us the very next arrangement in lexicographical order - there's no smaller arrangement between the current one and the next permutation. This guarantees we get the smallest possible larger palindrome.

The reason we can't just randomly rearrange digits is that we need to maintain the property that our result is larger than the original while being as small as possible. The next permutation algorithm achieves this by:

  1. Finding the rightmost position where we can make an increase
  2. Making the smallest possible increase at that position
  3. Arranging the remaining digits in ascending order to minimize the overall value

If no next permutation exists (when the first half is already in descending order like "321"), then we've exhausted all possible arrangements, and no larger palindrome can be formed by rearranging the digits.

Learn more about Two Pointers patterns.

Solution Approach

The solution implements the strategy of finding the next permutation of the first half of the palindrome string. Let's walk through the implementation step by step:

Main Function Structure:

The solution consists of two parts:

  1. A helper function next_permutation that finds the next lexicographically larger arrangement
  2. The main logic that applies this to create the palindrome

Next Permutation Algorithm:

The next_permutation function works on the first half of the digits:

  1. Find the pivot point (i): Starting from the second-to-last position of the first half (n - 2), scan leftward to find the first position where nums[i] < nums[i + 1]. This is the rightmost position where we can make an increase.

    i = n - 2
    while i >= 0 and nums[i] >= nums[i + 1]:
        i -= 1
  2. Check if permutation exists: If i < 0, all digits are in descending order, meaning we already have the largest possible permutation. Return False.

  3. Find the swap candidate (j): From the end of the first half, find the smallest digit that is still larger than nums[i].

    j = n - 1
    while j >= 0 and nums[j] <= nums[i]:
        j -= 1
  4. Swap and reverse: Swap nums[i] with nums[j], then reverse all digits after position i to get the smallest arrangement of the remaining digits.

    nums[i], nums[j] = nums[j], nums[i]
    nums[i + 1 : n] = nums[i + 1 : n][::-1]

Creating the Palindrome:

Once we have the next permutation of the first half:

  1. Convert the input string to a list for easier manipulation
  2. Apply next_permutation to get the next arrangement
  3. If successful, mirror the first half to create the second half:
    for i in range(n // 2):
        nums[n - i - 1] = nums[i]
  4. Join the list back into a string and return

The algorithm handles both even and odd-length palindromes correctly because:

  • For even-length palindromes, we work with exactly half the digits
  • For odd-length palindromes, the middle digit remains unchanged, and we still only need to rearrange the first half

Time Complexity: O(n) where n is the length of the string Space Complexity: O(n) for storing the list representation of the string

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 the solution with num = "12321", a 5-digit palindrome.

Step 1: Extract the first half

  • Length = 5, so first half length = 5 // 2 = 2
  • First half: positions [0, 1] = "12"
  • Convert entire string to list: ['1', '2', '3', '2', '1']

Step 2: Find next permutation of first half

We need to find the next permutation of indices [0, 1]:

a) Find pivot point (i):

  • Start at i = 0 (n-2 where n=2)
  • Check: nums[0] < nums[1]? β†’ '1' < '2'? Yes!
  • Found pivot at i = 0

b) Find swap candidate (j):

  • Start at j = 1 (n-1 where n=2)
  • Check: nums[1] > nums[0]? β†’ '2' > '1'? Yes!
  • Found swap candidate at j = 1

c) Perform swap:

  • Swap nums[0] and nums[1]
  • List becomes: ['2', '1', '3', '2', '1']

d) Reverse after pivot:

  • Reverse from position i+1 to n-1 (positions 1 to 1)
  • Only one element, so no change
  • List remains: ['2', '1', '3', '2', '1']

Step 3: Mirror the first half to create palindrome

  • Copy position 0 to position 4: nums[4] = '2'
  • Copy position 1 to position 3: nums[3] = '1'
  • Middle element (position 2) stays unchanged
  • Final list: ['2', '1', '3', '1', '2']

Step 4: Convert back to string

  • Result: "21312"

Verification:

  • Original: "12321"
  • Result: "21312"
  • Is it a palindrome? Yes (reads same forwards and backwards)
  • Is it larger? Yes (21312 > 12321)
  • Is it the smallest larger palindrome? Yes (next permutation guarantees this)

Edge Case Example:

If num = "32123":

  • First half: "32"
  • Already in descending order (3 β‰₯ 2)
  • No next permutation exists
  • Return: ""

This demonstrates why the algorithm correctly identifies when no larger palindrome can be formed by rearrangement.

Solution Implementation

1class Solution:
2    def nextPalindrome(self, num: str) -> str:
3        """
4        Find the next palindrome by rearranging the digits of the given number.
5      
6        Args:
7            num: A string representing a palindromic number
8          
9        Returns:
10            The next palindrome that can be formed by rearranging digits,
11            or empty string if no such palindrome exists
12        """
13      
14        def next_permutation(nums: list[str]) -> bool:
15            """
16            Find the next lexicographically greater permutation of the first half.
17            This modifies the list in-place.
18          
19            Args:
20                nums: List of digit characters (only first half is considered)
21              
22            Returns:
23                True if next permutation exists, False otherwise
24            """
25            # Only work with the first half of the palindrome
26            n = len(nums) // 2
27          
28            # Step 1: Find the rightmost position i where nums[i] < nums[i+1]
29            # This is the position that needs to be increased
30            i = n - 2
31            while i >= 0 and nums[i] >= nums[i + 1]:
32                i -= 1
33          
34            # If no such position exists, we've reached the largest permutation
35            if i < 0:
36                return False
37          
38            # Step 2: Find the rightmost position j where nums[j] > nums[i]
39            # This will be swapped with position i
40            j = n - 1
41            while j >= 0 and nums[j] <= nums[i]:
42                j -= 1
43          
44            # Step 3: Swap the elements at positions i and j
45            nums[i], nums[j] = nums[j], nums[i]
46          
47            # Step 4: Reverse the suffix starting at position i+1
48            # This gives us the smallest permutation greater than the original
49            nums[i + 1 : n] = nums[i + 1 : n][::-1]
50          
51            return True
52      
53        # Convert string to list for in-place modifications
54        nums = list(num)
55      
56        # Try to find the next permutation of the first half
57        if not next_permutation(nums):
58            return ""
59      
60        # Mirror the first half to create a palindrome
61        n = len(nums)
62        for i in range(n // 2):
63            nums[n - i - 1] = nums[i]
64      
65        # Convert back to string and return
66        return "".join(nums)
67
1class Solution {
2    /**
3     * Finds the next palindrome with the same set of digits
4     * @param num - input palindrome string
5     * @return next larger palindrome with same digits, or empty string if none exists
6     */
7    public String nextPalindrome(String num) {
8        // Convert string to character array for manipulation
9        char[] nums = num.toCharArray();
10      
11        // Try to find next permutation of the first half
12        if (!nextPermutation(nums)) {
13            // No next permutation exists
14            return "";
15        }
16      
17        // Mirror the first half to the second half to maintain palindrome property
18        int n = nums.length;
19        for (int i = 0; i < n / 2; ++i) {
20            nums[n - 1 - i] = nums[i];
21        }
22      
23        // Convert back to string and return
24        return String.valueOf(nums);
25    }
26
27    /**
28     * Generates the next lexicographically greater permutation of the first half
29     * @param nums - character array representing the palindrome
30     * @return true if next permutation exists, false otherwise
31     */
32    private boolean nextPermutation(char[] nums) {
33        // Only consider the first half of the palindrome
34        int n = nums.length / 2;
35      
36        // Step 1: Find the rightmost position where nums[i] < nums[i+1]
37        int i = n - 2;
38        while (i >= 0 && nums[i] >= nums[i + 1]) {
39            --i;
40        }
41      
42        // If no such position exists, we have the largest permutation
43        if (i < 0) {
44            return false;
45        }
46      
47        // Step 2: Find the smallest element to the right of position i that is greater than nums[i]
48        int j = n - 1;
49        while (j >= 0 && nums[i] >= nums[j]) {
50            --j;
51        }
52      
53        // Step 3: Swap elements at positions i and j
54        swap(nums, i, j);
55        i++;
56      
57        // Step 4: Reverse the sequence from position i+1 to the end of first half
58        j = n - 1;
59        while (i < j) {
60            swap(nums, i, j);
61            i++;
62            j--;
63        }
64      
65        return true;
66    }
67
68    /**
69     * Swaps two characters in the array
70     * @param nums - character array
71     * @param i - first index
72     * @param j - second index
73     */
74    private void swap(char[] nums, int i, int j) {
75        char temp = nums[i];
76        nums[i] = nums[j];
77        nums[j] = temp;
78    }
79}
80
1class Solution {
2public:
3    string nextPalindrome(string num) {
4        int n = num.size();
5      
6        // Extract the left half of the palindrome (excluding middle if odd length)
7        string leftHalf = num.substr(0, n / 2);
8      
9        // Try to find the next lexicographically greater permutation of the left half
10        // If no such permutation exists, return empty string
11        if (!next_permutation(begin(leftHalf), end(leftHalf))) {
12            return "";
13        }
14      
15        // Mirror the new left half to create the next palindrome
16        // Copy left half to both left and right sides of the result
17        for (int i = 0; i < n / 2; ++i) {
18            num[i] = leftHalf[i];           // Set left side character
19            num[n - i - 1] = leftHalf[i];   // Mirror to right side character
20        }
21      
22        return num;
23    }
24};
25
1/**
2 * Finds the next palindrome by finding the next permutation of the first half
3 * and mirroring it to create a palindrome
4 * @param num - The input number as a string
5 * @returns The next palindrome as a string, or empty string if not possible
6 */
7function nextPalindrome(num: string): string {
8    // Convert string to array of characters for manipulation
9    const digits: string[] = num.split('');
10    const length: number = digits.length;
11  
12    // Try to get the next permutation of the first half
13    if (!nextPermutation(digits)) {
14        return '';
15    }
16  
17    // Mirror the first half to the second half to create a palindrome
18    // Copy each digit from the first half to its corresponding position in the second half
19    for (let i = 0; i < length >> 1; ++i) {
20        digits[length - 1 - i] = digits[i];
21    }
22  
23    // Convert array back to string and return
24    return digits.join('');
25}
26
27/**
28 * Modifies the first half of the array to get the next lexicographically greater permutation
29 * @param digits - Array of digit characters to permute (modified in place)
30 * @returns true if next permutation exists, false otherwise
31 */
32function nextPermutation(digits: string[]): boolean {
33    // Only consider the first half of the digits for permutation
34    const halfLength: number = digits.length >> 1;
35  
36    // Step 1: Find the rightmost digit that is smaller than its next digit
37    // This is the pivot point for creating the next permutation
38    let pivotIndex: number = halfLength - 2;
39    while (pivotIndex >= 0 && digits[pivotIndex] >= digits[pivotIndex + 1]) {
40        pivotIndex--;
41    }
42  
43    // If no such digit exists, we're at the largest permutation
44    if (pivotIndex < 0) {
45        return false;
46    }
47  
48    // Step 2: Find the smallest digit to the right of pivot that is larger than pivot
49    let swapIndex: number = halfLength - 1;
50    while (swapIndex >= 0 && digits[pivotIndex] >= digits[swapIndex]) {
51        swapIndex--;
52    }
53  
54    // Step 3: Swap the pivot with the found digit
55    [digits[pivotIndex], digits[swapIndex]] = [digits[swapIndex], digits[pivotIndex]];
56  
57    // Step 4: Reverse the suffix after the pivot position to get the smallest permutation
58    let left: number = pivotIndex + 1;
59    let right: number = halfLength - 1;
60    while (left < right) {
61        [digits[left], digits[right]] = [digits[right], digits[left]];
62        left++;
63        right--;
64    }
65  
66    return true;
67}
68

Time and Space Complexity

The time complexity is O(n), where n is the length of the input string.

  • The next_permutation function performs operations on the first half of the string (length n/2):
    • The first while loop iterates at most n/2 times to find the pivot point: O(n/2)
    • The second while loop iterates at most n/2 times to find the swap position: O(n/2)
    • The swap operation is O(1)
    • The reversal operation nums[i + 1 : n][::-1] affects at most n/2 elements: O(n/2)
  • After next_permutation, the for loop runs n/2 times to mirror the first half to the second half: O(n/2)
  • The join operation to create the final string is O(n)

All these operations sum to O(n/2) + O(n/2) + O(n/2) + O(n/2) + O(n) = O(n).

The space complexity is O(n), where n is the length of the input string.

  • Converting the string to a list creates a new list of size n: O(n)
  • The slicing operation nums[i + 1 : n][::-1] creates a temporary list of at most n/2 elements: O(n/2)
  • The final string creation with join requires O(n) space

The dominant space usage is O(n) for storing the list representation of the string.

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

Common Pitfalls

1. Incorrectly Handling Odd-Length Palindromes

A common mistake is assuming that the middle character in odd-length palindromes needs special treatment during the next permutation calculation. This can lead to incorrect boundary calculations or attempting to include the middle character in the permutation logic.

Pitfall Example:

# Incorrect: Trying to include middle character in permutation
n = len(nums) // 2 + (len(nums) % 2)  # Wrong boundary for odd-length

Solution: The algorithm correctly uses n = len(nums) // 2 for both even and odd-length palindromes. The middle character (if it exists) remains unchanged and doesn't participate in the permutation:

n = len(nums) // 2  # Correct for both even and odd lengths

2. Off-by-One Errors in Index Calculations

When mirroring the first half to create the palindrome, it's easy to make indexing errors, especially with the relationship between i and its mirror position n - i - 1.

Pitfall Example:

# Incorrect mirroring
for i in range(n // 2):
    nums[n - i] = nums[i]  # Wrong: should be n - i - 1

Solution: Use the correct mirror index formula:

for i in range(n // 2):
    nums[n - i - 1] = nums[i]  # Correct mirroring

3. Not Preserving Original Input When No Solution Exists

Some implementations might partially modify the input array before determining that no next permutation exists, leading to corrupted results.

Pitfall Example:

def next_permutation(nums):
    # Modifying nums directly without checking if solution exists first
    nums[0], nums[1] = nums[1], nums[0]  # Premature modification
    # Then checking if valid...

Solution: The algorithm correctly checks for the existence of a valid pivot point (i) before making any modifications. If i < 0, it returns False immediately without changing the array.

4. Forgetting to Reverse the Suffix After Swapping

A critical step in the next permutation algorithm is reversing the suffix after the swap to ensure we get the smallest possible next permutation. Omitting this step results in a larger-than-necessary palindrome.

Pitfall Example:

# After swapping nums[i] and nums[j]
nums[i], nums[j] = nums[j], nums[i]
# Forgot to reverse the suffix - results in wrong answer

Solution: Always reverse the suffix after the swap:

nums[i], nums[j] = nums[j], nums[i]
nums[i + 1 : n] = nums[i + 1 : n][::-1]  # Essential step

5. String vs List Confusion

Attempting to modify a string directly (strings are immutable in Python) or forgetting to convert back to string at the end.

Pitfall Example:

# Trying to modify string directly
num[i] = num[j]  # Error: strings are immutable

Solution: Convert to list for manipulation, then back to string:

nums = list(num)  # Convert to mutable list
# ... perform operations ...
return "".join(nums)  # Convert back to string
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