1400. Construct K Palindrome Strings
Problem Description
You are given a string s
and an integer k
. Your task is to determine if you can use all the characters in s
to construct exactly k
non-empty palindrome strings.
A palindrome string reads the same forwards and backwards (like "aba", "racecar", or even single characters like "a").
You need to return true
if it's possible to use every character from s
to form exactly k
palindromes, or false
otherwise.
For example:
- If
s = "annabelle"
andk = 2
, you could form palindromes like "anna" and "belle" isn't a palindrome, but you could rearrange to form "anna" and "blleb", so this would returntrue
. - If
s = "abc"
andk = 1
, you cannot form a single palindrome using all three characters since they're all different.
The key insight is that in a palindrome, characters must appear in pairs (they can be mirrored), except for at most one character that can sit in the middle. So if you have too many characters with odd counts compared to the number of palindromes you need to form, it becomes impossible.
Intuition
Let's think about what makes a palindrome. In any palindrome, characters must appear in matching pairs that mirror each other, except for potentially one character in the middle (for odd-length palindromes).
For example:
- "aba" - the 'a' appears twice (forms a pair), 'b' appears once (sits in middle)
- "abba" - both 'a' and 'b' appear twice (form pairs)
- "racecar" - 'r', 'a', 'c' appear twice (form pairs), 'e' appears once (sits in middle)
This means each palindrome can accommodate at most one character with an odd count. If a character appears an odd number of times, one of those occurrences must be the "center" of some palindrome.
Now, if we need to form k
palindromes:
- We can have at most
k
characters with odd counts (one per palindrome as the center) - All other characters must have even counts so they can form pairs
Here's the key insight: If we count how many characters in our string s
appear an odd number of times, this tells us the minimum number of palindromes we must create. Why? Because each character with an odd count needs to have its "extra" occurrence placed as a center somewhere.
For instance, if we have 3 characters appearing odd times, we need at least 3 palindromes to accommodate these odd occurrences as centers.
The solution becomes clear:
- Count the frequency of each character in
s
- Count how many characters have odd frequencies
- If this count is
β€ k
, we can formk
palindromes (we can always split palindromes further if needed) - If this count is
> k
, it's impossible (not enough palindromes to hold all the odd-count characters)
Also, we need to check if len(s) < k
first - we can't form k
non-empty strings if we don't have at least k
characters total.
Learn more about Greedy patterns.
Solution Approach
The implementation follows directly from our intuition about counting characters with odd frequencies.
Step 1: Check minimum length constraint
First, we check if len(s) < k
. If the string has fewer characters than k
, it's impossible to create k
non-empty palindrome strings, so we immediately return false
.
if len(s) < k:
return False
Step 2: Count character frequencies
We use Python's Counter
from the collections module to count the frequency of each character in the string s
. This creates a hash table where keys are characters and values are their counts.
cnt = Counter(s)
For example, if s = "aabbbc"
, then cnt
would be {'a': 2, 'b': 3, 'c': 1}
.
Step 3: Count characters with odd frequencies
We iterate through all the frequency values and count how many are odd. The expression v & 1
is a bitwise operation that returns 1
if v
is odd and 0
if v
is even (checking the least significant bit).
sum(v & 1 for v in cnt.values())
This is equivalent to checking v % 2
but using bitwise AND is slightly more efficient.
Step 4: Compare with k
Finally, we check if the number of characters with odd frequencies is at most k
. If it is, we can distribute these odd-count characters as centers across our k
palindromes, and return true
. Otherwise, we return false
.
return sum(v & 1 for v in cnt.values()) <= k
Time Complexity: O(n)
where n
is the length of string s
, as we iterate through the string once to count frequencies.
Space Complexity: O(1)
or O(26)
to be precise, since we're only storing counts for at most 26 lowercase English letters (assuming the input contains only lowercase letters).
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 walk through the solution with s = "aabbbc"
and k = 2
.
Step 1: Check minimum length constraint
len(s) = 6
andk = 2
- Since
6 β₯ 2
, we can proceed
Step 2: Count character frequencies
- Count each character in "aabbbc":
- 'a' appears 2 times
- 'b' appears 3 times
- 'c' appears 1 time
- So
cnt = {'a': 2, 'b': 3, 'c': 1}
Step 3: Count characters with odd frequencies
- Check each frequency:
- 'a': 2 is even β contributes 0
- 'b': 3 is odd β contributes 1
- 'c': 1 is odd β contributes 1
- Total odd-frequency characters = 1 + 1 = 2
Step 4: Compare with k
- We have 2 characters with odd frequencies
- We need to form k = 2 palindromes
- Since 2 β€ 2, we return
true
Why this works: We can form two palindromes by placing one odd-count character as the center of each:
- Palindrome 1: "bab" (using one 'b' as center)
- Palindrome 2: "abca" (using 'c' as center)
Or alternatively:
- Palindrome 1: "aba" (using one 'b' as center)
- Palindrome 2: "bcb" (using 'c' as center)
Each palindrome accommodates exactly one character with an odd count as its center, which is why having 2 odd-count characters works perfectly for forming 2 palindromes.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def canConstruct(self, s: str, k: int) -> bool:
5 # If string length is less than k, we cannot form k palindromes
6 if len(s) < k:
7 return False
8
9 # Count frequency of each character in the string
10 char_count = Counter(s)
11
12 # Count how many characters have odd frequency
13 # For a palindrome, at most one character can have odd frequency
14 # With k palindromes, we can have at most k characters with odd frequency
15 odd_freq_count = sum(freq & 1 for freq in char_count.values())
16
17 # Check if number of odd frequency characters is at most k
18 return odd_freq_count <= k
19
1class Solution {
2 public boolean canConstruct(String s, int k) {
3 // Get the length of the input string
4 int stringLength = s.length();
5
6 // If string length is less than k, we cannot form k palindromes
7 // (each palindrome needs at least one character)
8 if (stringLength < k) {
9 return false;
10 }
11
12 // Array to count frequency of each character (26 lowercase letters)
13 int[] characterFrequency = new int[26];
14
15 // Count the frequency of each character in the string
16 for (int i = 0; i < stringLength; i++) {
17 characterFrequency[s.charAt(i) - 'a']++;
18 }
19
20 // Count characters with odd frequency
21 // In a palindrome, at most one character can have odd frequency (the middle character)
22 int oddFrequencyCount = 0;
23 for (int frequency : characterFrequency) {
24 // Check if frequency is odd using bitwise AND with 1
25 oddFrequencyCount += frequency & 1;
26 }
27
28 // We can construct k palindromes if the number of characters with odd frequency
29 // is at most k (each palindrome can accommodate at most one odd-frequency character)
30 return oddFrequencyCount <= k;
31 }
32}
33
1class Solution {
2public:
3 bool canConstruct(string s, int k) {
4 // If string length is less than k, we cannot form k palindromes
5 if (s.size() < k) {
6 return false;
7 }
8
9 // Count frequency of each character
10 int charFrequency[26] = {0};
11 for (char& ch : s) {
12 charFrequency[ch - 'a']++;
13 }
14
15 // Count characters with odd frequency
16 int oddFrequencyCount = 0;
17 for (int frequency : charFrequency) {
18 // Check if frequency is odd using bitwise AND
19 if (frequency & 1) {
20 oddFrequencyCount++;
21 }
22 }
23
24 // We can form k palindromes if the number of characters with odd frequency
25 // is at most k (each palindrome can have at most one character with odd frequency)
26 return oddFrequencyCount <= k;
27 }
28};
29
1/**
2 * Determines if string s can be constructed into k palindromes
3 * @param s - The input string to be divided into palindromes
4 * @param k - The number of palindromes to construct
5 * @returns true if s can be constructed into exactly k palindromes, false otherwise
6 */
7function canConstruct(s: string, k: number): boolean {
8 // Cannot form k palindromes if string length is less than k
9 // (each palindrome needs at least one character)
10 if (s.length < k) {
11 return false;
12 }
13
14 // Array to count frequency of each letter (26 letters in alphabet)
15 const charFrequency: number[] = new Array(26).fill(0);
16
17 // Count the frequency of each character in the string
18 for (const char of s) {
19 const charIndex = char.charCodeAt(0) - 'a'.charCodeAt(0);
20 charFrequency[charIndex]++;
21 }
22
23 // Count characters with odd frequency
24 // In a palindrome, at most one character can have odd frequency (the middle character)
25 let oddFrequencyCount = 0;
26 for (const frequency of charFrequency) {
27 // Use bitwise AND to check if frequency is odd (frequency & 1 returns 1 if odd, 0 if even)
28 oddFrequencyCount += frequency & 1;
29 }
30
31 // We can form k palindromes if the number of characters with odd frequency
32 // is at most k (each palindrome can have at most one odd-frequency character)
33 return oddFrequencyCount <= k;
34}
35
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of string s
. This is because:
- Creating a Counter object requires iterating through all characters in the string once:
O(n)
- The
sum(v & 1 for v in cnt.values())
iterates through the values in the counter, which has at mostmin(n, 26)
entries (since there are only 26 lowercase letters). This takesO(26)
=O(1)
time in the worst case
The space complexity is O(C)
, where C
is the size of the character set. In this case, C = 26
since we're dealing with lowercase English letters. The Counter object stores at most 26 key-value pairs (one for each unique character that appears in the string), regardless of how long the input string is.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting the Upper Bound Check
The Problem:
Many developers focus only on checking if there are too many characters with odd frequencies (odd_freq_count <= k
) but forget to verify if the string is long enough to form k
palindromes in the first place.
Incorrect Implementation:
def canConstruct(self, s: str, k: int) -> bool:
char_count = Counter(s)
odd_freq_count = sum(freq & 1 for freq in char_count.values())
return odd_freq_count <= k # Missing length check!
Why It Fails:
For input s = "ab"
and k = 3
, this would incorrectly return True
because there are only 2 odd-frequency characters (both 'a' and 'b' appear once). However, you cannot create 3 non-empty palindromes from just 2 characters.
The Fix:
Always check if len(s) >= k
before proceeding with the odd frequency logic:
if len(s) < k:
return False
Pitfall 2: Misunderstanding the Odd Frequency Constraint
The Problem: Some might think that having odd-frequency characters means the solution is impossible, or they might incorrectly calculate the relationship between odd frequencies and palindrome count.
Incorrect Logic Examples:
# Wrong: Thinking any odd frequency makes it impossible
return all(freq % 2 == 0 for freq in char_count.values())
# Wrong: Checking if odd count equals k exactly
return odd_freq_count == k
Why It Fails:
- First example: Single-character palindromes are valid (like "a"), so odd frequencies are perfectly fine.
- Second example: If
s = "aabbcc"
andk = 2
, all characters have even frequency (odd_freq_count = 0), but we can still form 2 palindromes like "abc" and "abc" or "aabbcc" split differently.
The Fix:
Remember that each palindrome can accommodate at most one character with odd frequency (as its center). So with k
palindromes, you can have at most k
characters with odd frequencies:
return odd_freq_count <= k
Pitfall 3: Overcomplicated Distribution Logic
The Problem:
Attempting to actually distribute characters into k
palindromes or trying to construct the palindromes explicitly.
Overcomplicated Approach:
def canConstruct(self, s: str, k: int) -> bool:
# Trying to actually build the palindromes
palindromes = [[] for _ in range(k)]
char_count = Counter(s)
# Complex distribution logic...
for char, count in char_count.items():
# Trying to evenly distribute pairs
pairs = count // 2
for i in range(pairs):
palindromes[i % k].append(char)
palindromes[i % k].append(char)
# Handle remaining odd character
if count % 2 == 1:
# More complex logic...
Why It's Unnecessary:
The problem only asks if it's possible to construct k
palindromes, not to actually construct them. The mathematical constraint (odd_freq_count <= k) is sufficient to determine possibility.
The Fix: Stick to the simple counting approach. The beauty of this problem is that you don't need to construct anythingβjust verify the constraints are satisfied.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
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!