409. Longest Palindrome
Problem Description
In this problem, we're given a string s
containing a mix of lowercase and uppercase letters. Our task is to determine the maximum length of a palindrome that can be created using the letters from the string. It's important to note that case matters here; 'A' and 'a' are treated as different characters, which means a string like "Aa" wouldn't count as a palindrome.
Intuition
To solve this problem, we can use the fact that a palindrome is symmetrical around its center. This symmetry means that most letters have to appear an even number of times in the string, so they can be mirrored on both sides of the palindrome. The only exception is the center of the palindrome, which can hold a single character if the length of the palindrome is odd.
With this in mind, we can iterate over the counts of each letter in the string. For each letter:
- If it has an even count, we can use all occurrences of that letter in the palindrome.
- If it has an odd count, we can use all but one occurrence of that letter to maintain symmetry.
Additionally, we can place exactly one character with an odd count in the center of the palindrome (if we haven't already placed a center character). To handle this gracefully, we can use a greedy approach: always add characters in pairs to the palindrome, and if there is no center yet and we encounter an odd count, we place one of those characters at the center.
The implementation uses a Counter
to count occurrences of each character in s
. We then iterate over these counts, and for each:
- We add to the answer the largest even number that is less than or equal to the count of the character. This is achieved by
v - (v & 1)
which subtracts one ifv
is odd. - We potentially add one more to the answer (for the center character) if there isn't already a center character. This is determined by
ans & 1 ^ 1
which is true ifans
is even, signifying that we haven't added a center character yet, andv & 1
which is true ifv
is odd.
Learn more about Greedy patterns.
Solution Approach
The implementation starts by counting the frequency of each character in the given string s
. This is done using the Counter
class from Python's collections
module. The Counter
class creates a dictionary-like object where the keys are the characters from s
, and the values are the counts of those characters.
cnt = Counter(s)
We then initialize the ans
variable to 0, which will serve as our answer to hold the length of the longest palindrome we can build.
ans = 0
Next, we iterate over the values in cnt
(which are the counts of each character in the string) and for each value v
:
- We want to add as many characters as possible to the palindrome while maintaining its symmetrical structure. To accomplish this, we add the largest even number smaller than or equal to
v
to our answerans
. This is done by using the expressionv - (v & 1)
, which subtracts 1 fromv
ifv
is odd, effectively giving us the largest even number.
ans += v - (v & 1)
- We need to consider the possibility of a central character in the palindrome. We can afford to add one such character if we haven't already added one. The expression
(ans & 1 ^ 1)
checks ifans
is still even. If it is, it means we haven't placed a center character in the palindrome yet, and(v & 1)
checks ifv
is odd, indicating that this character could potentially be the center. If both conditions are true, we add 1 toans
, thereby adding a central character.
ans += (ans & 1 ^ 1) and (v & 1)
Finally, we return the ans
, which gives us the length of the longest palindrome that can be created with the characters from s
.
The overall algorithm is a greedy one, as it tries to use as many characters as possible while respecting the condition that only one character can be in the middle of a palindrome if the length is odd. By using a Counter and iterating through its values, we efficiently consider each unique character without having to check the entire string multiple times.
class Solution:
def longestPalindrome(self, s: str) -> int:
cnt = Counter(s)
ans = 0
for v in cnt.values():
ans += v - (v & 1)
ans += (ans & 1 ^ 1) and (v & 1)
return ans
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution approach with a small example. Suppose our input string s
is "AabbcC"
.
-
We first count the frequency of each character using the
Counter
class.- The
Counter(s)
would give us{'A': 1, 'a': 1, 'b': 2, 'c': 1, 'C': 1}
.
- The
-
Now we initialize our answer
ans
to 0. -
We start iterating over the character counts:
- For
'A'
with a count of 1:ans += 1 - (1 & 1)
which adds 0 as 'A' has an odd count, and we can't have a pair yet. Then,ans += (ans & 1 ^ 1) and (1 & 1)
will add 1, becauseans
is even, and 'A' could potentially be at the center. - For
'a'
with a count of 1: We again add 0 for pairs of 'a'. Since we already have a center character, we don't add another. - For
'b'
with a count of 2:ans += 2 - (2 & 1)
adds 2 as 'b' has an even count, we can use both. - For
'c'
with a count of 1: Analogously to 'A', we add 0 for pairs and do not add to the center as we already have one. - For
'C'
with a count of 1: Same as with 'c', we add 0 for pairs and nothing to the center.
- For
-
The final
ans
is 3, representing the longest palindrome"AbA"
, which we can build from the input strings
.
So, from input string "AabbcC"
, the maximum length palindrome we can create is 3, and the palindrome could be "AbA"
.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def longestPalindrome(self, s: str) -> int:
5 # Count the occurrences of each character in the input string
6 char_count = Counter(s)
7 # Initialize the length of the longest palindrome to be 0
8 longest_palindrome_length = 0
9
10 # Iterate through the counts of each character
11 for count in char_count.values():
12 # Add the largest even number less than or equal to 'count' to the
13 # length of the longest palindrome. This represents the maximum
14 # number of characters that can be used in both halves of the palindrome.
15 longest_palindrome_length += count - (count % 2)
16
17 # Ensure that there is a center character if one has not been chosen already:
18 # Check if longest_palindrome_length is currently even and if 'count' is odd.
19 # If conditions satisfy, increase the longest palindrome length by 1
20 # to include one of the odd-count characters as the center of the palindrome.
21 longest_palindrome_length += ((longest_palindrome_length % 2 == 0) and (count % 2 == 1))
22
23 # Return the length of the longest palindrome that can be built with the characters
24 return longest_palindrome_length
25
1class Solution {
2 public int longestPalindrome(String s) {
3 // Create an array to count occurrences of each character.
4 // The ASCII value of a character will be used as the index.
5 int[] charCounts = new int[128];
6 for (int i = 0; i < s.length(); i++) {
7 // Count each character's occurrences.
8 charCounts[s.charAt(i)]++;
9 }
10
11 int lengthOfLongestPalindrome = 0;
12 for (int count : charCounts) {
13 // Add the largest even number below or equal to the current character count.
14 // This is equivalent to count - (count % 2).
15 lengthOfLongestPalindrome += count - (count & 1);
16
17 // If the current palindrome length is even and the count is odd,
18 // we can add one more character to the center of the palindrome.
19 if (lengthOfLongestPalindrome % 2 == 0 && count % 2 == 1) {
20 lengthOfLongestPalindrome++;
21 }
22 }
23 // Return the length of the longest possible palindrome.
24 return lengthOfLongestPalindrome;
25 }
26}
27
1#include <string> // Include the string header for using the std::string type
2
3class Solution {
4public:
5 int longestPalindrome(std::string s) {
6 // Create an array to hold the count of each character.
7 // 128 covers all ASCII characters which include standard English letters, digits, and punctuation.
8 int charCount[128] = {};
9
10 // Count the occurrence of each character in the string.
11 for (char c : s) {
12 charCount[c]++;
13 }
14
15 int maxLength = 0; // Initialize the length of the longest palindrome to 0.
16
17 // Iterate through the character counts.
18 for (int count : charCount) {
19 // Add the largest even number that is less than or equal to the current count.
20 // This is because even counts can be mirrored on both sides of a palindrome.
21 maxLength += count - (count % 2);
22
23 // If the current maxLength is even and there's an odd count of characters,
24 // we can add one of those characters to the center of the palindrome.
25 // Only one center character is allowed for a palindrome, hence the check (maxLength % 2 == 0).
26 if (maxLength % 2 == 0 && count % 2 == 1) {
27 maxLength++;
28 }
29 }
30
31 // Return the length of the longest palindrome that can be created.
32 return maxLength;
33 }
34};
35
1function longestPalindrome(s: string): number {
2 let lengthOfString = s.length;
3 let lengthOfLongestPalindrome = 0;
4 let charCount = new Array(128).fill(0); // array to store character frequencies
5
6 // Count the frequency of each character in the string.
7 for (let i = 0; i < lengthOfString; i++) {
8 charCount[s.charCodeAt(i)]++;
9 }
10
11 // Iterate over the character frequency array.
12 for (let i = 0; i < 128; i++) {
13 let frequency = charCount[i];
14 // If the frequency is even, add it to the lengthOfLongestPalindrome, since
15 // even counts of a character can be placed symmetrically in the palindrome.
16 // If the frequency is odd, the maximum even count that can be used is frequency - 1.
17 lengthOfLongestPalindrome += frequency % 2 == 0 ? frequency : frequency - 1;
18 }
19
20 // If the length of the constructed palindrome is less than the length of the original string,
21 // we can add one more character (center of the palindrome).
22 // This character does not need to have a matching pair.
23 return lengthOfLongestPalindrome < lengthOfString ? lengthOfLongestPalindrome + 1 : lengthOfLongestPalindrome;
24}
25
Time and Space Complexity
Time Complexity
The time complexity of the given code is primarily determined by the traversal through the characters of the string s
and the values in the cnt
(counter) object.
-
The first operation is creating a frequency counter (
cnt
) for the characters in the string withCounter(s)
which takesO(n)
time wheren
is the length of the strings
. -
The second part involves iterating through the values of the
cnt
object. The number of unique characters ins
will be at mostk
, wherek
is the size of the character set (such as 26 for lowercase English letters, etc.), thus this iteration isO(k)
.
Combining these steps, since k
can be at most n
when all characters are unique, the overall time complexity is O(n)
.
Space Complexity
The space complexity of the code includes the space required for storing the frequency of each character in the string s
.
- The
Counter(s)
creates a dictionary with at mostk
key-value pairs wherek
is the number of unique characters ins
. Hence, the space required for this isO(k)
.
Since k
is the number of unique characters and k
can be at most n
, the space complexity is also O(n)
if we assume that the input string can have a large and varied set of characters.
Overall, the code has a time complexity of O(n)
and a space complexity of O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
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
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!