2384. Largest Palindromic Number
Problem Description
Given a string num
that contains only digits, you need to find the largest possible palindromic integer that can be formed by rearranging the digits from num
. The result should be returned as a string and must not have leading zeros.
A palindrome reads the same forwards and backwards. For example, "12321" is a palindrome.
Key requirements:
- You can use any subset of the digits from
num
(but must use at least one digit) - You can rearrange the digits in any order
- The result must be a valid palindrome
- The result should be the largest possible palindromic number
- No leading zeros are allowed (except for the special case of "0" itself)
For example:
- If
num = "129321"
, you could form "9219" as a palindrome by using digits 9, 2, 1, 9 - If
num = "444947137"
, you could form "7449447" by arranging the digits appropriately
The solution approach counts the frequency of each digit, then builds the palindrome by:
- Finding the largest odd-frequency digit to potentially place in the middle
- Using pairs of digits symmetrically on both sides, starting from the largest digits
- Handling the special case of leading zeros by stripping them (returning "0" if all digits are zeros)
Intuition
To form the largest palindrome, we need to think about the structure of palindromes and how to maximize the numerical value.
A palindrome has a mirror structure - the left half mirrors the right half. For even-length palindromes like "1221", every digit appears an even number of times. For odd-length palindromes like "12321", one digit can appear an odd number of times (the middle digit), while all others must appear an even number of times.
To maximize the value, we want larger digits in the most significant positions. Since a palindrome mirrors itself, placing a digit on the left automatically determines its position on the right. For example, if we place '9' at the leftmost position, we must also place '9' at the rightmost position.
This leads us to a greedy strategy:
- Count how many times each digit appears in the input
- For the middle position (if the palindrome has odd length), we can use one digit that appears an odd number of times - we should choose the largest such digit
- For the symmetric pairs on both sides, we can use half of each digit's count, placing larger digits towards the outside to maximize the value
The key insight is that we build the palindrome from inside out. First, we identify if there's a middle digit (any digit with odd frequency, preferably the largest). Then we add pairs of digits symmetrically, starting from the smallest digits (which go near the middle) to the largest digits (which go on the outside).
The special case of leading zeros needs attention - if our constructed palindrome is all zeros except possibly a middle digit, we need to return just "0" instead of "000...000".
Learn more about Greedy patterns.
Solution Approach
The implementation uses a frequency counter to track digit occurrences and builds the palindrome strategically:
-
Count digit frequencies: Use
Counter(num)
to count how many times each digit ('0' through '9') appears in the input string. -
Find the middle digit: Iterate from the largest digit '9' down to '0' to find the largest digit with an odd frequency. This digit will be placed in the middle of our palindrome:
for i in range(9, -1, -1): v = str(i) if cnt[v] % 2: ans = v cnt[v] -= 1 break
We decrement its count by 1 since we're using one instance for the middle position.
-
Build symmetric pairs: Iterate from the smallest digit '0' to the largest '9'. For each digit with remaining count:
for i in range(10): v = str(i) if cnt[v]: cnt[v] //= 2 s = cnt[v] * v ans = s + ans + s
- Divide the count by 2 (using integer division) to get the number of pairs
- Create a string
s
with that many instances of the digit - Add
s
to both sides of the current answer:s + ans + s
This builds the palindrome from inside out - smaller digits are placed closer to the middle, larger digits on the outside.
-
Handle leading zeros: The final step
ans.strip('0') or '0'
removes leading zeros from both ends. If the result becomes empty (all digits were zeros), return '0'.
The algorithm runs in O(n) time where n is the length of the input string, with O(1) extra space since we only store counts for 10 possible digits.
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 trace through the solution with num = "129321"
:
Step 1: Count digit frequencies
- '1': 2 times
- '2': 2 times
- '3': 1 time
- '9': 1 time
Step 2: Find the middle digit (largest odd-frequency digit)
- Start from '9': frequency is 1 (odd) β
- Take one '9' for the middle:
ans = "9"
- Update count: '9' now has 0 remaining
Step 3: Build symmetric pairs from inside out
- Check '0': count is 0, skip
- Check '1': count is 2
- Pairs to use: 2 Γ· 2 = 1 pair
- Create string:
s = "1"
(one '1') - Add symmetrically:
ans = "1" + "9" + "1" = "191"
- Check '2': count is 2
- Pairs to use: 2 Γ· 2 = 1 pair
- Create string:
s = "2"
(one '2') - Add symmetrically:
ans = "2" + "191" + "2" = "21912"
- Check '3': count is 1
- Pairs to use: 1 Γ· 2 = 0 pairs (integer division)
- Skip (no pairs to add)
- Check '4' through '8': count is 0, skip
- Check '9': count is 0 (already used for middle), skip
Step 4: Handle leading zeros
- "21912" has no leading zeros
- Final answer:
"21912"
Note how the algorithm builds from inside out: the middle '9' was placed first, then pairs of '1' were added around it, and finally pairs of '2' were added on the outside. This ensures larger digits are in more significant positions, creating the largest possible palindrome.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def largestPalindromic(self, num: str) -> str:
5 # Count the frequency of each digit in the input string
6 digit_count = Counter(num)
7
8 # Initialize the result palindrome with empty string (will be used as middle character if needed)
9 middle_char = ''
10
11 # Find the largest digit with odd count to place in the middle
12 # Starting from 9 down to 0 ensures we get the largest possible middle digit
13 for digit in range(9, -1, -1):
14 digit_str = str(digit)
15 if digit_count[digit_str] % 2 == 1:
16 middle_char = digit_str
17 digit_count[digit_str] -= 1 # Decrease count by 1 as we use it for middle
18 break
19
20 # Build the palindrome by placing pairs of digits symmetrically
21 for digit in range(10):
22 digit_str = str(digit)
23 if digit_count[digit_str] > 0:
24 # Use half of the remaining count for each side
25 digit_count[digit_str] //= 2
26 half_string = digit_str * digit_count[digit_str]
27 # Place the digits symmetrically around the middle character
28 middle_char = half_string + middle_char + half_string
29
30 # Remove leading zeros from both ends (except when the result is just "0")
31 # If after stripping we get empty string, return "0"
32 return middle_char.strip('0') or '0'
33
1class Solution {
2 public String largestPalindromic(String num) {
3 // Count frequency of each digit (0-9)
4 int[] digitFrequency = new int[10];
5 for (char c : num.toCharArray()) {
6 digitFrequency[c - '0']++;
7 }
8
9 // Find the largest digit with odd frequency to place in the middle
10 // We want the largest one for maximum value
11 String middleDigit = "";
12 for (int digit = 9; digit >= 0; digit--) {
13 if (digitFrequency[digit] % 2 == 1) {
14 middleDigit = String.valueOf(digit);
15 digitFrequency[digit]--; // Use one occurrence for middle
16 break; // Only need one middle digit
17 }
18 }
19
20 // Build the first half of the palindrome
21 // Use half of the remaining even-count digits
22 StringBuilder firstHalf = new StringBuilder();
23 for (int digit = 0; digit <= 9; digit++) {
24 if (digitFrequency[digit] > 0) {
25 // Divide frequency by 2 to get half for each side
26 int halfCount = digitFrequency[digit] / 2;
27 // Append the digit 'halfCount' times
28 for (int i = 0; i < halfCount; i++) {
29 firstHalf.append(digit);
30 }
31 }
32 }
33
34 // Remove trailing zeros from first half to avoid leading zeros in result
35 // (except when the entire number would be "0")
36 while (firstHalf.length() > 0 && firstHalf.charAt(firstHalf.length() - 1) == '0') {
37 firstHalf.deleteCharAt(firstHalf.length() - 1);
38 }
39
40 // Save the first half before reversing
41 String secondHalf = firstHalf.toString();
42
43 // Build the complete palindrome: reversed_first_half + middle + first_half
44 String result = firstHalf.reverse().toString() + middleDigit + secondHalf;
45
46 // Special case: if result is empty, return "0"
47 return result.isEmpty() ? "0" : result;
48 }
49}
50
1class Solution {
2public:
3 string largestPalindromic(string num) {
4 // Count frequency of each digit (0-9)
5 vector<int> digitCount(10, 0);
6 for (char c : num) {
7 digitCount[c - '0']++;
8 }
9
10 // Find the largest odd-count digit for middle position
11 string middle = "";
12 for (int digit = 9; digit >= 0; digit--) {
13 if (digitCount[digit] % 2 == 1) {
14 middle += (char)(digit + '0');
15 digitCount[digit]--;
16 break;
17 }
18 }
19
20 // Build the first half of palindrome using pairs
21 string firstHalf = "";
22 for (int digit = 0; digit <= 9; digit++) {
23 if (digitCount[digit] > 0) {
24 // Use half of the remaining count (pairs)
25 int pairCount = digitCount[digit] / 2;
26 while (pairCount > 0) {
27 firstHalf += (char)(digit + '0');
28 pairCount--;
29 }
30 }
31 }
32
33 // Remove leading zeros from first half
34 while (!firstHalf.empty() && firstHalf.back() == '0') {
35 firstHalf.pop_back();
36 }
37
38 // Construct the palindrome: reverse(firstHalf) + middle + firstHalf
39 string result = firstHalf;
40 reverse(result.begin(), result.end());
41 result += middle + firstHalf;
42
43 // Handle edge case: if result is empty, return "0"
44 return result.empty() ? "0" : result;
45 }
46};
47
1/**
2 * Finds the largest palindromic number that can be formed from the given digits
3 * @param num - String containing digits to form the palindrome
4 * @returns The largest palindromic number as a string
5 */
6function largestPalindromic(num: string): string {
7 // Count frequency of each digit (0-9)
8 const digitFrequency: number[] = new Array(10).fill(0);
9 for (const digit of num) {
10 digitFrequency[parseInt(digit)]++;
11 }
12
13 // Remove digits with odd frequency until at most one digit has odd frequency
14 // This ensures we can form a valid palindrome
15 while (digitFrequency.reduce((oddCount, freq) => freq % 2 === 1 ? oddCount + 1 : oddCount, 0) > 1) {
16 for (let i = 0; i < 10; i++) {
17 if (digitFrequency[i] % 2 === 1) {
18 digitFrequency[i]--;
19 break;
20 }
21 }
22 }
23
24 // Build the first half of the palindrome
25 const firstHalf: number[] = [];
26 let middleDigit: number = -1;
27
28 // Process digits from 9 to 0 to ensure largest palindrome
29 for (let digit = 9; digit >= 0; digit--) {
30 // Check if this digit will be the middle element (odd frequency)
31 if (digitFrequency[digit] % 2 === 1) {
32 middleDigit = digit;
33 digitFrequency[digit]--;
34 }
35 // Add half of the remaining digits to the first half
36 const halfCount: number = digitFrequency[digit] >> 1;
37 for (let j = 0; j < halfCount; j++) {
38 firstHalf.push(digit);
39 }
40 }
41
42 // Add middle digit if exists
43 if (middleDigit !== -1) {
44 firstHalf.push(middleDigit);
45 }
46
47 // Handle leading zeros - find first non-zero digit
48 const totalLength: number = firstHalf.length;
49 for (let i = 0; i < totalLength; i++) {
50 if (firstHalf[i] !== 0) {
51 // Remove leading zeros
52 const trimmedFirstHalf: number[] = firstHalf.slice(i);
53
54 // Build complete palindrome by mirroring the first half
55 let palindrome: number[];
56 if (middleDigit !== -1) {
57 // If there's a middle digit, don't include it in the mirror
58 const mirrorPart: number[] = [...trimmedFirstHalf.slice(0, trimmedFirstHalf.length - 1)].reverse();
59 palindrome = [...trimmedFirstHalf, ...mirrorPart];
60 } else {
61 // No middle digit, mirror the entire first half
62 const mirrorPart: number[] = [...trimmedFirstHalf].reverse();
63 palindrome = [...trimmedFirstHalf, ...mirrorPart];
64 }
65
66 return palindrome.join('');
67 }
68 }
69
70 // All digits are zeros
71 return '0';
72}
73
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs the following operations:
Counter(num)
takesO(n)
time wheren
is the length of the input string, as it needs to count the frequency of each character- The first loop iterates from 9 to 0 (10 iterations), which is
O(1)
constant time - The second loop iterates from 0 to 9 (10 iterations), and for each iteration with count
cnt[v]
, it creates a string of lengthcnt[v]
and performs string concatenation. Since we're dealing with digits 0-9, the total number of characters processed across all iterations is still bounded byn
, making thisO(n)
strip('0')
operation takesO(n)
time in the worst case
Overall time complexity: O(n) + O(1) + O(n) + O(n) = O(n)
Space Complexity: O(n)
The space usage includes:
Counter
object storing at most 10 key-value pairs:O(1)
- The
ans
string which can grow up to lengthn
in the worst case:O(n)
- Temporary string
s
created during concatenation, which in total across all iterations uses space proportional to the input size:O(n)
Overall space complexity: O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Building Order Leading to Suboptimal Results
The most critical pitfall in this solution is building the palindrome from smallest to largest digits (0 to 9), which places larger digits closer to the middle and smaller digits on the outside. This produces a smaller palindrome instead of the largest possible one.
Example: For input "129321"
, the current approach would:
- Find middle digit: '1' (odd count)
- Build from 0 to 9: places '2' pairs around '1', then '9' pairs around that
- Result:
"92129"
instead of the optimal"99199"
Solution: Reverse the building order to iterate from 9 down to 0:
# Instead of:
for digit in range(10):
# builds smaller palindrome
# Use:
for digit in range(9, -1, -1):
digit_str = str(digit)
if digit_count[digit_str] > 0:
digit_count[digit_str] //= 2
half_string = digit_str * digit_count[digit_str]
middle_char = half_string + middle_char + half_string
2. Not Handling All-Zero Edge Case Properly
When the input consists entirely of zeros (e.g., "0000"
), the algorithm might attempt to build a palindrome like "000"
which, after stripping leading zeros, becomes an empty string. The or '0'
handles this, but developers might forget this edge case.
Solution: Always ensure the final return statement includes proper handling:
return middle_char.strip('0') or '0'
3. Misunderstanding the Strip Operation
Using strip('0')
removes zeros from both ends, which is correct for palindromes. However, developers might mistakenly use lstrip('0')
thinking they only need to remove leading zeros, forgetting that in a palindrome, trailing zeros are also leading zeros when read backwards.
4. Not Considering Single Non-Zero Digit with Multiple Zeros
For input like "00001000"
, after building the palindrome, you might get "10001"
. The algorithm should recognize that using just "1"
is actually the largest valid palindrome since "10001"
has leading zeros when considered as a number.
Solution: After stripping zeros, verify the result is still a valid palindrome and hasn't broken the symmetry:
result = middle_char.strip('0') or '0' # Ensure the result maintains palindrome property after stripping
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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!