Longest Palindrome
EasyPrompt
Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters.
Letters are case sensitive, for example, "Aa" is not considered a palindrome.
Example 1:
Input: s = "abccccdd"
Output: 7
Explanation: One longest palindrome that can be built is "dccaccd", whose length is 7.Example 2:
Input: s = "a"
Output: 1
Explanation: The longest palindrome that can be built is "a", whose length is 1.
Constraints:
1 <= s.length <= 2000sconsists of lowercase and/or uppercase English letters only.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves using a HashMap to store the frequency of each character in the input string. We iterate through the string once to populate the map. Then, we iterate through the map's values (the character counts) to construct the length of the longest palindrome based on the counts.
Algorithm
- Initialize a
HashMap<Character, Integer>to store character frequencies. - Iterate through the input string
sand populate the frequency map. For each characterc, increment its count in the map. - Initialize
length = 0and a boolean flagoddFound = false. - Iterate through the values (frequencies) of the map.
- For each frequency
count, if it's even, add the fullcounttolength. If it's odd, addcount - 1tolengthand setoddFoundtotrue. - After the loop, if
oddFoundistrue, it means we can place a single character in the center of the palindrome, so we incrementlengthby 1. - Return the final
length.
Walkthrough
The core idea is that a palindrome is built from pairs of characters, with at most one unique character in the center. We can count the occurrences of each character to determine how many pairs can be formed.
- We create a
HashMap<Character, Integer>to store the counts of each character. - We loop through the input string
s. For each character, we update its count in the map. - After counting, we initialize a variable
lengthto 0 and a boolean flagoddFoundtofalse. - We iterate through the counts in the map. For each count
c:- We add the largest even number less than or equal to
cto ourlength. This is done by addingcifcis even, orc - 1ifcis odd. - If we encounter any character with an odd count, we set the
oddFoundflag totrue.
- We add the largest even number less than or equal to
- Finally, if
oddFoundistrue, it means we have at least one leftover character that can be placed in the center of the palindrome. We add 1 to the totallength. - The final
lengthis the answer.
import java.util.HashMap;import java.util.Map; class Solution { public int longestPalindrome(String s) { if (s == null || s.length() == 0) { return 0; } Map<Character, Integer> counts = new HashMap<>(); for (char c : s.toCharArray()) { counts.put(c, counts.getOrDefault(c, 0) + 1); } int length = 0; boolean oddFound = false; for (int count : counts.values()) { if (count % 2 == 0) { length += count; } else { length += count - 1; oddFound = true; } } if (oddFound) { length++; } return length; }}Complexity
Time
O(N), where N is the length of the string `s`. Populating the HashMap requires iterating through the string once. Iterating through the map's values takes O(K) time, where K is the number of unique characters (a constant, at most 52).
Space
O(K), where K is the number of unique characters in the string. Since the problem specifies lowercase and uppercase English letters, K is at most 52. Therefore, the space complexity is effectively O(1).
Trade-offs
Pros
The logic is straightforward and easy to understand.
It is a general solution that works for any character set (e.g., Unicode), not just English letters.
Cons
Has higher overhead compared to using a simple array due to the costs of hashing and
Map.Entryobject creation.Requires two passes: one over the string to build the map, and another over the map's values to calculate the length.
This approach cleverly uses a HashSet to count pairs of characters in a single pass through the string. The set is used to keep track of characters that have appeared an odd number of times so far. When a character is encountered that is already in the set, we know we've formed a pair.
Algorithm
- Initialize a
HashSet<Character>to keep track of unpaired characters. - Initialize a variable
pairs = 0to count the number of character pairs found. - Iterate through each character
cof the strings. - If the
HashSetalready containsc, it means we've found a matching pair. Incrementpairsand removecfrom the set. - If the
HashSetdoes not containc, it's an unpaired character so far. Addcto the set. - After the loop, the total length from pairs is
pairs * 2. - If the
HashSetis not empty, it means there's at least one leftover character that can be the center of the palindrome. Add 1 to the total length. - Return the final length.
Walkthrough
This method avoids explicitly counting frequencies and instead focuses on forming pairs directly.
- We initialize a
HashSet<Character>calledoddCharsand an integerpairsto 0. - We iterate through the input string
scharacter by character. - For each character
c:- We check if
cis present inoddChars. If it is, we have found a pair. We incrementpairsby 1 and removecfrom the set, as it's now part of a pair. - If
cis not inoddChars, it's currently an unpaired character. We add it to the set to wait for its match.
- We check if
- After the loop,
pairsholds the total number of character pairs. The length contributed by these pairs ispairs * 2. - The
oddCharsset now contains all characters that appeared an odd number of times in the string. If the set is not empty, we can use one of these characters as the center of our palindrome, adding 1 to the total length. - The result is
pairs * 2plus 1 ifoddCharsis not empty.
import java.util.HashSet;import java.util.Set; class Solution { public int longestPalindrome(String s) { if (s == null || s.length() == 0) { return 0; } Set<Character> oddChars = new HashSet<>(); int pairs = 0; for (char c : s.toCharArray()) { if (oddChars.contains(c)) { pairs++; oddChars.remove(c); } else { oddChars.add(c); } } int length = pairs * 2; if (!oddChars.isEmpty()) { length++; } return length; }}Complexity
Time
O(N), where N is the length of the string. We iterate through the string once, and each `HashSet` operation (add, remove, contains) takes O(1) time on average.
Space
O(K), where K is the number of unique characters. Since K is at most 52, this is O(1).
Trade-offs
Pros
Processes the string in a single pass.
The logic is elegant and avoids a second loop over counts.
More efficient than the HashMap approach as it avoids storing full counts.
Cons
Still incurs some overhead from hashing, making it slightly less performant than a direct-access array.
This is the most efficient approach for this problem. Given that the character set is limited to lowercase and uppercase English letters, we can use a simple array as a direct-access map to count character frequencies. This method is faster than using a HashMap or HashSet because it avoids the overhead of hashing.
Algorithm
- Initialize an integer array
countsof size 128 to all zeros. This array will map ASCII values to their frequencies. - Iterate through the input string
s. For each characterc, increment the count at its corresponding ASCII index:counts[c]++. - Initialize
length = 0. - Iterate through the
countsarray. For eachcount, add(count / 2) * 2tolength. This sums up the contributions of all character pairs. - After the loop, if the calculated
lengthis less than the original string's length (s.length()), it implies there was at least one character with an odd count. We can add one such character to the center, so we incrementlengthby 1. - Return
length.
Walkthrough
The problem statement guarantees that the input string only contains English letters. This allows us to use a fixed-size array for frequency counting, which is extremely fast.
- We declare an integer array
countsof size 128. This size is sufficient to cover the ASCII values of all uppercase ('A'-'Z') and lowercase ('a'-'z') letters. - We iterate through the input string
s. For each characterc, we use its ASCII value as an index into thecountsarray and increment the value at that index (counts[c]++). - After counting, we calculate the length of the palindrome. We initialize
length = 0. - We iterate through the
countsarray. For eachcount, we add(count / 2) * 2tolength. This effectively discards at most one occurrence of each character (if the count is odd) and sums up the rest. - After the loop,
lengthholds the maximum possible length from paired characters. If thislengthis smaller than the original string length, it means there was at least one character with an odd count. We can use one of these as a central character, so we add 1 to thelength. - The final
lengthis the answer.
class Solution { public int longestPalindrome(String s) { int[] counts = new int[128]; // 'z' has ASCII value 122 for (char c : s.toCharArray()) { counts[c]++; } int length = 0; for (int count : counts) { length += (count / 2) * 2; } if (length < s.length()) { length++; // Add a single character for the center if there are any leftovers } return length; }}Complexity
Time
O(N), where N is the length of the string `s`. The first loop to populate the frequency array is O(N). The second loop to calculate the length runs a fixed number of times (128), making it an O(1) operation. Thus, the total time complexity is dominated by the first loop.
Space
O(1), as the `counts` array has a fixed size of 128, which does not depend on the input string's length.
Trade-offs
Pros
Extremely fast due to direct array indexing, which avoids any hashing overhead.
Minimal space overhead with a small, fixed-size array.
The implementation is simple and concise.
Cons
This specific implementation is tailored to the ASCII character set and would need modification for larger character sets like Unicode.
Solutions
Solution
class Solution {public int longestPalindrome(String s) { int[] cnt = new int[128]; for (int i = 0; i < s.length(); ++i) { ++cnt[s.charAt(i)]; } int ans = 0; for (int v : cnt) { ans += v - (v & 1); if (ans % 2 == 0 && v % 2 == 1) { ++ans; } } return ans; }}class Solution {public: int longestPalindrome(string s) { int cnt[128]{}; for (char &c : s) { ++cnt[c]; } int ans = 0; for (int v : cnt) { ans += v - (v & 1); if (ans % 2 == 0 && v % 2 == 1) { ++ans; } } return ans; }};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 ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.