Smallest Palindromic Rearrangement I โ ๐ด Advanced (3/3) โ LeetCode Daily Python Solution (why not just try every order?)
๐ Jump to your level: ๐ด Advanced: 0:20โ3:56 ๐ Full written solution: https://interview-kit-fe.vercel.app/smallest-palindromic-rearrangement-i-advanced ๐ป Code on GitHub: https://github.com/timepasshub2539-hue/leetcode-python/tree/main/smallest-palindromic-rearrangement-i-advanced Chapters: 0:00 The Problem: Smallest Palindromic Rearrangement I 0:20 ๐ด Advanced: Half the string is redundant information 0:38 This shortcut also powers spell-check and DNA repair 0:51 The ask: same letters, smallest palindrome 1:04 The only tools we need 1:16 Why not just try every arrangement? 1:29 The Half and Mirror trick 1:42 Step one: tally the letters 1:58 Step two: build the smallest half 2:19 Step three: mirror it back 2:33 Your turn: pause and solve it 2:38 The reveal 2:50 Could we have brute-forced 'tacocat'? 3:07 Factorial versus linear 3:24 Edge cases it survives 3:46 Recap: count, split, mirror, done 3:56 Quick Quiz! 4:03 ... 4:06 Answer! 4:13 Round 2! 4:21 ... 4:24 Answer! We solve LeetCode's Smallest Palindromic Rearrangement I by realizing half the letters carry all the information โ the same shortcut behind spell-check and DNA repair. You'll see why brute-forcing every arrangement of a word like 'tacocat' explodes factorially, then build the answer in three linear steps: tally, build the smallest half, mirror it back. Includes edge cases, a full walkthrough, and two quick quizzes to test yourself. #LeetCode #Python #CodingInterview #DSA #Algorithms Watch next: - Maximum Product of Two Elements in an Array โ ๐ด Advanced (3/3) โ LeetCode Python Solution (One Pass Trick): https://youtu.be/hJgrE0-D-l4 - Maximum Product of Three Numbers โ ๐ด Advanced (3/3) โ LeetCode Daily Python Solution (the two-negative trap): https://youtu.be/xmBpGBa7TEQ - Maximum Product of Two Digits โ ๐ด Advanced (3/3) โ LeetCode Daily Python Solution (swap one digit, +60): https://youtu.be/p2wpVqhqd9s
Introduction
Palindrome problems show up constantly in coding interviews because they test something more specific than "can you write a loop." They test whether you can spot a hidden constraint that shrinks your search space. Smallest Palindromic Rearrangement is a great example: on the surface it looks like a permutation problem, and permutation problems have a bad reputation for exploding into factorial time. But the moment you notice the input has to stay a palindrome, half the string stops being a decision you make โ it becomes a mirror of the other half.
This is worth learning well beyond the interview room. The same "reduce to the free half" idea shows up in nearest-valid-string problems, in DNA sequence repair algorithms, and generally anywhere you're asked to find the closest or smallest object inside a constrained space rather than an unconstrained one. Interviewers use this problem to check whether you reach for brute force out of habit, or whether you look for the constraint first.
Problem Overview
You're given a string that is guaranteed to already be a palindrome. Your job is to rearrange its letters โ using every letter exactly once, no additions or removals โ so that the result is:
- Still a palindrome, and
- The lexicographically smallest such palindrome possible.
"Lexicographically smallest" just means smallest in dictionary order โ the way words are sorted in a dictionary, comparing letter by letter from the left.
Because the input is already a palindrome, you know something important going in: at most one letter in the whole string can have an odd count. Every other letter appears an even number of times. That's not a coincidence โ it's a mathematical property of every palindrome, and it's the key that unlocks the whole problem.
Example
Take the string tacocat.
Count the letters: t:2, a:2, c:2, o:1.
Only o has an odd count, so o must sit in the exact center of the result โ there's no other place it can go without breaking the palindrome property.
Everything else needs to be split into two equal, mirrored halves. Take the remaining letters t, t, a, a, c, c, halve each count (t:1, a:1, c:1), and sort them alphabetically to get the smallest possible left half: act.
Mirror that half around the center letter:
act + o + tca
Result: actotca.
Check it: is it a palindrome? Read it backwards โ actotca reversed is actotca. Yes. Is it the smallest one possible? Since we built the left half using the smallest available letters in alphabetical order, nothing smaller could exist without changing which letters are available โ and the multiset of letters is fixed.
Intuition
Here's how an experienced engineer approaches this, before writing a single line of code.
Start with the naive instinct: "generate every rearrangement, keep the ones that are palindromes, take the smallest." That's correct, but it's also wildly wasteful, and a good engineer's first move is to ask: what structure does this problem hand me for free?
The structure here is symmetry. A palindrome is completely defined by its first half (plus, if the length is odd, one center character). The second half isn't an independent choice โ it's forced to be the mirror image of the first. That means the "real" problem isn't "arrange n characters into a palindrome." It's "arrange n/2 characters into the smallest possible string," which is a much easier problem you've solved before: sort them.
The only wrinkle is the middle character when the length is odd. Since the input is guaranteed to already be a palindrome, exactly one letter (or zero, if the length is even) has an odd count. That letter has nowhere else to go โ it must be the center, because pairing it with anything else would break the even-count requirement for a valid palindrome.
So the recipe writes itself:
- Count every letter.
- Pull out the (at most one) odd-count letter as the center.
- Halve every other count, sort those letters alphabetically โ that's your left half.
- Mirror the left half to build the right half.
No search. No candidates to filter. Just counting and sorting.
Brute Force Solution
Idea: Generate all n! permutations of the string, filter to the ones that are valid palindromes, and take the minimum.
Algorithm:
- Generate every permutation of the input characters.
- For each one, check if it equals its own reverse.
- Track the smallest palindromic permutation seen.
Advantages: Dead simple to reason about and impossible to get wrong โ it's a literal restatement of the problem.
Disadvantages: Combinatorially explosive. Even a 7-character string like tacocat produces 5,040 permutations before filtering. At the problem's actual constraint (up to 100,000 characters), this approach doesn't just get slow โ it becomes physically impossible to run, since n! for even moderately sized n exceeds the number of atoms in the observable universe.
Time complexity: O(n! ยท n) โ factorial permutations, each requiring O(n) to check and compare. Space complexity: O(n! ยท n) if you materialize all candidates, or O(n) if you generate lazily and track only the best one so far โ still useless at scale because of the time cost.
Optimal Solution
The optimal approach never generates a candidate string at all โ it constructs the answer directly.
Step 1 โ Count. Build a frequency map of every character using a Counter. This is a single O(n) pass.
Step 2 โ Find the center. Scan the counts for the one with an odd value. Because the input is guaranteed to be a valid palindrome, there's at most one. If none exists, there's no center character (even-length palindrome).
Step 3 โ Build the smallest half. For every letter, take count // 2 copies. Sort the resulting letters alphabetically โ since earlier letters in the alphabet always produce a smaller string when placed earlier, sorting directly gives you the lexicographically smallest arrangement of that half-multiset.
Step 4 โ Mirror. Concatenate: half + center + reversed(half).
A quick table for tacocat:
| Letter | Count | Half count | Center? |
|---|---|---|---|
| t | 2 | 1 | no |
| a | 2 | 1 | no |
| c | 2 | 1 | no |
| o | 1 | 0 | yes |
Sorted half: a, c, t โ act. Final: act + o + tca = actotca.
Python Code
from collections import Counter
def smallest_palindromic_rearrangement(s: str) -> str:
counts = Counter(s)
center = ""
half_letters = []
for letter, count in counts.items():
if count % 2 == 1:
center = letter # guaranteed at most one, since s is a palindrome
half_letters.extend(letter * (count // 2))
half_letters.sort()
half = "".join(half_letters)
return half + center + half[::-1]
Code Walkthrough
Counter(s)builds the frequency map in one O(n) pass โ no manual dictionary bookkeeping needed.- The loop walks each distinct letter once. If its count is odd, it's stored as
center. Since the input is guaranteed to be a palindrome, this branch fires at most once across the whole loop. half_letters.extend(letter * (count // 2))addscount // 2copies of the letter to the half-string's character pool. Integer division naturally drops the odd leftover character (which is already accounted for bycenter).half_letters.sort()arranges the pool alphabetically โ this is the greedy step that guarantees lexicographic minimality.- The return line assembles the answer: sorted half, then center, then the same half reversed to mirror it.
Dry Run
Input: tacocat
Counter("tacocat")โ{'t': 2, 'a': 2, 'c': 2, 'o': 1}- Loop through the counts:
t: even โ add"t"once tohalf_lettersa: even โ add"a"oncec: even โ add"c"onceo: odd โcenter = "o", add nothing (0 copies)
half_letters = ['t', 'a', 'c']โ sorted โ['a', 'c', 't']half = "act"- Result:
"act" + "o" + "tca"="actotca"
Complexity Analysis
Time: O(n log n) โ dominated by sorting the half-string, which has at most n/2 characters. Counting is O(n). Space: O(n) โ for the counter and the half-string buffer.
This is correct because every step touches each character a bounded number of times: once to count it, at most once to place it in the half-string, and once during the sort comparison chain. Nothing revisits the full input more than a constant number of times, and the sort is the only super-linear step.
Alternative Solutions
You could skip building an explicit list and instead sort the distinct letters, then generate the half string letter-by-letter using string multiplication and "".join(). This is functionally identical and doesn't change the complexity class โ it's a style preference, not a different algorithm. There isn't a fundamentally different approach worth comparing here, because once you accept the half-symmetry argument, sorting is provably optimal (see the exchange argument below) and nothing beats O(n log n) for that step.
Edge Cases
- Empty string: Counter is empty, loop doesn't fire,
halfis empty,centeris empty. Returns"". Correct. - Single character: One odd count,
halfstays empty, result is just that character. Correct. - All identical characters (e.g.,
"aaaa"): One even count, half becomes"aa", no center, mirrors to"aaaa". The construction is a no-op on already-uniform input. - Already the smallest arrangement: No special-casing needed โ the algorithm always rebuilds from scratch and naturally reproduces the optimal string.
- Maximum length input (100,000 characters): O(n log n) sorting keeps this comfortably fast; no risk of timeout.
Common Mistakes
- Assuming multiple odd counts are possible. The problem guarantees the input is already a palindrome, so this can't happen โ but if you reuse this code on arbitrary strings, it silently picks the last odd-count letter it encounters as the center and drops the rest, which is a bug waiting to surface outside this problem's guarantees.
- Sorting the full string instead of the half. This produces the wrong answer because it ignores the mirroring constraint entirely.
3Forgetting integer division drops the odd remainder. Using
count / 2instead ofcount // 2in Python introduces a float and breaks string multiplication. - Rebuilding the right half independently instead of reversing the left half. This risks subtle bugs if the two halves ever drift out of sync โ always derive the mirror from the same source.
- Not sorting before mirroring. Building the half in insertion order instead of alphabetical order silently produces a valid palindrome that isn't the smallest one.
Interview Questions
- What if the input weren't guaranteed to be a valid palindrome โ how would you detect that no rearrangement is possible?
- Can you prove that sorting the half-multiset actually produces the lexicographically smallest result? (Exchange argument: swapping any two out-of-order letters in the half can only make the result larger or equal, never smaller.)
- How would this change if you needed the largest palindromic rearrangement instead of the smallest?
- What's the complexity if you needed to generate the k-th smallest palindromic rearrangement instead of just the first?
Similar Problems
- Palindrome Permutation (LeetCode 266): Checks whether any palindrome permutation exists โ the odd-count-parity check used here in isolation.
- Palindrome Permutation II (LeetCode 267): Asks for all distinct palindromic permutations, extending the half-construction idea into a full generation problem.
- Longest Palindromic Substring (LeetCode 5): A different palindrome family โ searching within a string rather than constructing one from a multiset.
- Smallest Palindromic Rearrangement II: The natural follow-up (referenced in the video) โ the harder variant where a simple mirror isn't enough on its own.
Key Takeaways
A constraint that reduces your degrees of freedom is a signal to reduce your algorithm's complexity class, not just optimize its constant factor. Recognizing that a palindrome is fully determined by its first half turns an n! search into an O(n log n) construction. The core interview skill being tested isn't palindrome trivia โ it's the habit of asking "what does this constraint actually buy me?" before reaching for brute force.
Watch the Video
Prefer to see this walked through step by step, with the two quick quizzes included? Watch the full explanation here: {LINK}
About the Series
This breakdown is part of Fun with Learning Technology, a series that works through LeetCode Daily challenges by focusing on the reasoning behind each solution โ not just the final code. Every video builds intuition first, so the pattern sticks with you well beyond the specific problem.
Call To Action
If this explanation helped the idea click, give the video a like on YouTube and subscribe so you don't miss the next problem in the series โ tomorrow tackles the harder rearrangement variant where the mirror trick alone isn't enough. Subscribe to the Substack for the written breakdowns delivered straight to your inbox, drop a comment with your own approach or a question, and share this with anyone prepping for coding interviews.
The solution
half = []
mid = ""
for ch in sorted(cnt):
c = cnt[ch]
if c % 2:
mid = ch
half.append(ch * (c // 2))
half = "".join(half)Ready to try it yourself? Solve Greedy problems with instant feedback in the practice sandbox.
Practice now โ


