Smallest Palindromic Rearrangement I ā š¢ Beginner (1/3) ā LeetCode Daily Python Solution (the trick nobody sees)
š Jump to your level: š¢ Beginner: 0:21ā6:19 š Full written solution: https://interview-kit-fe.vercel.app/smallest-palindromic-rearrangement-i-beginner š» Code on GitHub: https://github.com/timepasshub2539-hue/leetcode-python/tree/main/smallest-palindromic-rearrangement-i-beginner Chapters: 0:00 The Problem: Smallest Palindromic Rearrangement I 0:21 š¢ Beginner: Good news: half the string writes itself 0:43 The same trick shows up in spell-check and even DNA 1:01 The task: same letters, smallest palindrome 1:24 The only tools we need 1:42 Why not just try every possible arrangement? 2:10 The Half and Mirror trick 2:37 Step one: count every letter 2:56 Step two: build the smallest half 3:26 Step three: mirror it back 3:42 Your turn: pause and try it yourself 3:55 The reveal 4:18 Could we have just brute-forced 'tacocat'? 4:39 Factorial versus linear: why speed matters here 5:13 Tricky cases it handles without extra work 5:48 Recap: count, split, mirror, done We solve LeetCode's Smallest Palindromic Rearrangement I with one clean idea: count the letters, build the smallest half, mirror it back. No brute force, no factorial explosion, just a counting trick that shows up everywhere from spell-check to DNA analysis. You'll see why trying every arrangement of 'tacocat' is a trap, how the Half and Mirror method handles odd-length strings and repeated letters without extra code, and how to walk through it yourself before the reveal. Beginner-friendly, step by step, in Python. #LeetCode #Python #CodingInterview #DailyLeetCode #Algorithms Watch next: - Maximum Product of Two Elements in an Array ā š¢ Beginner (1/3) ā LeetCode Python Solution (why [2,2] returns 1): https://youtu.be/UQU2ctAeB3I - Maximum Product of Three Numbers ā š¢ Beginner (1/3) ā LeetCode Python Solution (the negative trap): https://youtu.be/oFykTRn46Gg - Maximum Product of Two Digits ā š¢ Beginner (1/3) ā LeetCode Daily Python Solution (the trick nobody sees): https://youtu.be/LcqYDC9_CSI
Introduction
Palindrome problems show up constantly in coding interviews because they test something more specific than "do you know a palindrome is." They test whether you can see structure in a string that most people only see as a flat sequence of characters. Smallest Palindromic Rearrangement I is a perfect example. On the surface it looks like a sorting or brute-force problem. Underneath, it's really a lesson in symmetry ā and once you see it, you'll start noticing the same "only think about half the problem" pattern in run-length encoding, DNA sequence correction, and even autocomplete systems.
This problem is also a great one to learn early because the trap is so tempting. Your first instinct will almost certainly be to generate every possible arrangement of the letters and filter for the palindromes. That instinct isn't wrong ā it's just going to cost you the interview the moment the interviewer says "what if the string has 100,000 characters?" Learning to recognize when a full-arrangement approach quietly becomes impossible is one of the most transferable skills in this entire subject.
Problem Overview
Here's the problem in plain terms: you're given a string that is already a palindrome ā meaning it reads the same forwards and backwards, like babab or tacocat. Your job is to rearrange its letters (you can't add, remove, or change any letter) into the alphabetically smallest string that is also a palindrome.
"Alphabetically smallest" works the same way it does when you sort words in a dictionary. abbba comes before babab because a comes before b. You're not inventing a new string ā you're just finding the best possible ordering of the letters you were already given.
Example
Take the string babab. It has three bs and two as. If we rearrange those same five letters into abbba, we still have three bs and two as, and the string reads identically forward and backward. Compare abbba to other valid palindromic rearrangements like babab or bbabb ā abbba is the smallest because its very first character, a, beats every other candidate's first character, b.
Now try tacocat. Counting letters gives us t:2, a:2, c:2, o:1. The only letter with an odd count, o, must sit in the exact middle of the palindrome ā every other letter has to be split evenly across the two mirrored halves. Arranging the remaining letters alphabetically for the left half gives act, so the finished palindrome is actotca.
Intuition
Here's the mental shift that makes this problem easy: a palindrome is completely determined by its left half. The right half isn't independent information ā it's a forced mirror image of the left half. If you imagine a mirror standing in the exact center of the string, whatever you place on the left automatically appears (flipped) on the right.
That means the entire problem ā "find the smallest palindrome" ā quietly shrinks into a much smaller problem: "find the smallest possible left half." Whatever you build there gets copied for free.
So how do you build the smallest possible left half? The same way you'd build the smallest possible string out of a bag of letters: use the smallest letters first. If you have two as and three bs, you want a appearing before b in your left half, because a is alphabetically smaller.
There's one wrinkle. Since the original string is already a palindrome, at most one letter can have an odd count (that's just a mathematical property of palindromes ā every letter except possibly one has to have a partner on the other side of the center). That one odd-count letter can't go in either half ā it belongs in the very middle, alone. Everything else gets split evenly: half its count goes into your left-half build, and the mirror handles the rest automatically.
Once you see this, the algorithm writes itself: count the letters, pull out the (at most one) odd-count letter as the middle character, take half of every other count in alphabetical order to build the left half, then glue it all together as left + middle + reverse(left).
Brute Force Solution
Idea: Generate every distinct permutation of the string's letters, keep only the ones that are palindromes, sort the survivors, and return the smallest one.
Algorithm:
- Generate all unique permutations of the string.
- For each permutation, check if it reads the same forwards and backwards.
- Collect all palindromic permutations.
- Return the smallest one alphabetically.
Advantages: Dead simple to reason about. Works fine for tiny strings, and it's a natural starting point if you're just trying to understand the problem.
Disadvantages: The number of permutations of a string grows as n! (n factorial). For tacocat (7 letters), that's already 5,040 arrangements ā annoying but doable. For a 20-letter string, it's over 2 quintillion. For LeetCode's actual constraints (strings up to 100,000 characters), this approach doesn't just get slow ā it becomes mathematically impossible to finish, even with unlimited computing power.
Time complexity: O(n! Ć n) ā factorial arrangements, each requiring O(n) to check and compare. Space complexity: O(n! Ć n) to store all the permutations.
Optimal Solution
The Half and Mirror approach turns the factorial blowup into a single linear pass. Here's the breakdown:
| Step | What happens | Why it matters |
|---|---|---|
| 1. Count | Tally every letter's frequency | Tells us exactly how many of each letter we're working with |
| 2. Find the odd one out | At most one letter will have an odd count | That letter is forced into the exact middle |
| 3. Build the half | For each letter (alphabetical order), take count // 2 copies |
Smallest letters first = smallest possible left half |
| 4. Mirror | answer = half + middle + reversed(half) |
The right side is never computed ā it's just a copy |
Because we iterate through letters in alphabetical order (a to z), the left half we build is automatically the smallest possible arrangement of those characters ā there's no separate sorting step needed for the output, since we're constructing it in order from the start.
Python Code
from collections import Counter
def smallest_palindrome(s: str) -> str:
"""Rearrange a palindrome's letters into the smallest palindrome possible."""
counts = Counter(s)
half_chars = []
middle_char = ""
for letter in sorted(counts):
count = counts[letter]
if count % 2 == 1:
middle_char = letter
half_chars.append(letter * (count // 2))
half = "".join(half_chars)
return half + middle_char + half[::-1]
Code Walkthrough
Counter(s)builds a frequency map in one pass ā{'t': 2, 'a': 2, 'c': 2, 'o': 1}fortacocat.sorted(counts)iterates the keys of the counter alphabetically, guaranteeing we place smaller letters earlier in the left half.count % 2 == 1catches the single odd-count letter (guaranteed to be at most one, since the input is already a palindrome) and saves it asmiddle_char.letter * (count // 2)appends half of that letter's total count to the half-string ā integer division naturally drops the odd leftover, which is already accounted for bymiddle_char.half[::-1]reverses the completed left half to produce the mirrored right side ā no extra logic needed for letters that never got an odd count.- Final assembly
half + middle_char + half[::-1]glues the three pieces into the finished palindrome. If there's no middle character,middle_charis just an empty string and disappears from the concatenation.
Dry Run
Input: tacocat
Counter("tacocat")ā{'t': 2, 'a': 2, 'c': 2, 'o': 1}- Iterate sorted keys:
a,c,o,ta: count 2, even ā append"a"(1 copy)c: count 2, even ā append"c"(1 copy)o: count 1, odd ā setmiddle_char = "o", append""(0 copies)t: count 2, even ā append"t"(1 copy)
half = "act"- Result:
"act" + "o" + "tca"ā"actotca"
Matches the expected answer from the video.
Complexity Analysis
Time: O(n) ā one pass to build the counter, one pass over the fixed 26-letter alphabet to build the half (constant, since there are only 26 possible letters), and one pass to reverse and concatenate. All of this is linear in the length of the input string.
Space: O(n) ā the output string and the half-string are each proportional to the input length; the counter itself only ever holds at most 26 entries.
These bounds are correct because every step touches each character (or each of the 26 possible letters) exactly once ā there's no nested loop, no re-scanning, and no exponential branching anywhere in the algorithm.
Alternative Solutions
You could technically sort the entire string, split it into two mirrored halves, and check parity separately ā but that's strictly more work than counting, since sorting costs O(n log n) versus the O(n) counting approach. Counting wins because we only care about frequencies, not the actual relative order of every character in the original string.
Edge Cases
- Single character (
"z"): counts to{'z': 1}. No even-count letters, sohalfstays empty andmiddle_char = "z". Result:"z". - All identical letters (
"aaaa"):half = "aa", no middle character. Result:"aaaa". - Already the smallest palindrome: rebuilding it from its own letter counts reproduces the identical string ā the algorithm is idempotent.
- Even-length input with no odd-count letters (
"abba"):middle_charstays empty, andhalf + "" + half[::-1]still produces a correct even-length palindrome. - Long strings with many repeated letters (100,000 characters): performance stays linear; nothing about the algorithm depends on string length beyond the single counting and building passes.
Common Mistakes
- Forgetting that at most one letter can have an odd count and mishandling multiple "middle" candidates ā this can't happen with valid input, but skipping the check entirely leads to bugs if you reuse this logic for non-palindromic inputs.
- Sorting the string directly instead of sorting on the counted letters ā this wastes time and can scramble the pairing logic.
- Appending the full count instead of half the count to the left-side builder, which doubles the letters in the final output.
- Forgetting to reverse the half before mirroring, producing a string that isn't actually a palindrome.
- Placing the middle character in the wrong position ā it must sit between the half and its reverse, not at the start or end.
- Assuming brute force is "fine" without checking constraints ā 5,040 permutations feels manageable, but the same code silently becomes unusable at scale.
Interview Questions
- "What if the input string isn't guaranteed to be a palindrome ā can you still rearrange it into one?" (Hint: check if more than one letter has an odd count; if so, it's impossible.)
- "Can you do this in-place, without building a new string?"
- "How would this change if you needed the largest palindrome instead of the smallest?"
- "What's the time complexity if the alphabet isn't fixed at 26 letters (e.g., Unicode)?"
Similar Problems
- LeetCode 409 ā Longest Palindrome: uses the same "at most one odd count" insight to determine the longest palindrome constructible from a set of letters.
- LeetCode 1400 ā Construct K Palindrome Strings: extends the odd-count counting logic to multiple palindromes at once.
- LeetCode 267 ā Palindrome Permutation II: generates all distinct palindromic permutations, a good next step after mastering the single-answer version.
- Smallest Palindromic Rearrangement II (tomorrow's video in this series): builds directly on today's mirroring technique for a harder variant.
Key Takeaways
A palindrome is fully described by its left half ā the right half is free once you have the left. Counting letters and splitting each count in half turns an exponential brute-force search into a single linear pass. Whenever you're stuck on a symmetry-based problem, ask yourself: "am I solving twice the work I actually need to?"
Watch the Video
Prefer to see this walked through visually, with the dry run drawn out step by step? Watch the full explanation here: {LINK}
About the Series
This walkthrough is part of Fun with Learning Technology, a daily series that breaks down LeetCode problems into their core intuition first ā no memorized templates, no rushing to code. Each video builds on ideas from the ones before it, so patterns like Half and Mirror keep showing up in smarter, harder forms as the series continues.
Call To Action
If this clicked for you, the video walks through the same dry run with visuals that make the mirroring click even faster ā give it a watch and a like on YouTube. Subscribe so you don't miss tomorrow's follow-up, which pushes this same trick into a tougher rearrangement problem. If you'd rather read than watch, subscribe to the Substack for the written breakdown of every problem in the series. And if you got stuck anywhere or have a cleaner approach, drop it in the comments ā sharing this with someone prepping for interviews helps more than you'd think.
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 ā


