Facebook Pixel

409. Longest Palindrome

EasyGreedyHash TableString
Leetcode Link

Problem Description

You are given a string s that consists of lowercase or uppercase letters. Your task is to find the length of the longest palindrome that can be built using those letters.

A palindrome is a string that reads the same forwards and backwards. When building the palindrome, you can rearrange the letters from the input string in any order, but you can only use each letter the number of times it appears in the original string.

Note that letters are case-sensitive, meaning 'A' and 'a' are considered different characters. For example, "Aa" is not a valid palindrome because the two characters are different.

The key insight is that in a palindrome:

  • Characters that appear in pairs can be placed symmetrically (one on each side)
  • At most one character can appear an odd number of times (it would go in the center)

For example, if s = "abccccdd", you can build palindromes like "dccaccd" or "ccdadcc" with length 7. The character 'a' or 'b' (which appear once) can be placed in the center, while the other characters form symmetric pairs.

The solution counts the occurrences of each character, uses as many pairs as possible (by taking v // 2 * 2 for each count v), and adds 1 to the result if there's any character with an odd count that can be placed in the center.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To build the longest palindrome, we need to understand the structure of palindromes. A palindrome reads the same forwards and backwards, which means characters must appear in matching pairs on both sides of the center.

Let's think about what makes a valid palindrome:

  • Even-length palindrome: Every character appears an even number of times (e.g., "abba", "aabbaa")
  • Odd-length palindrome: Exactly one character appears an odd number of times in the center, while all others appear in pairs (e.g., "aba", "racecar")

This leads us to a greedy strategy: we want to use as many characters as possible in pairs. For each character that appears n times:

  • If n is even, we can use all n occurrences (placing n/2 on each side)
  • If n is odd, we can use n-1 occurrences as pairs (placing (n-1)/2 on each side)

The expression v // 2 * 2 elegantly captures this logic - it gives us the largest even number less than or equal to v. For example:

  • If v = 4: 4 // 2 * 2 = 2 * 2 = 4 (use all 4)
  • If v = 5: 5 // 2 * 2 = 2 * 2 = 4 (use 4 out of 5)

After using all possible pairs, we check if there's any leftover character (when ans < len(s)). If there is, we can place exactly one character in the center of our palindrome, increasing our answer by 1. This works because a palindrome can have at most one character in its center position.

Solution Approach

The implementation uses a counting approach with a hash table to track character frequencies.

Step 1: Count Character Frequencies
We use Python's Counter from the collections module to count occurrences of each character in the string s. This creates a hash table cnt where keys are characters and values are their frequencies.

Step 2: Calculate Pairs
We iterate through all frequency values in cnt.values(). For each frequency v:

  • Calculate v // 2 * 2 to get the maximum even number โ‰ค v
  • This represents how many of that character we can use in pairs
  • Sum all these values to get the total length from paired characters

The expression sum(v // 2 * 2 for v in cnt.values()) efficiently computes this in one line.

Step 3: Add Center Character (if possible)
After using all possible pairs, we check if we can add one more character to the center:

  • If ans < len(s), it means we have at least one character with an odd count that wasn't fully used
  • We can place one such character in the center, so we add 1 to our answer
  • The expression int(ans < len(s)) converts the boolean to 1 if true, 0 if false

Example Walkthrough:
For s = "abccccdd":

  • Count: {'a': 1, 'b': 1, 'c': 4, 'd': 2}
  • Pairs: 1//2*2 + 1//2*2 + 4//2*2 + 2//2*2 = 0 + 0 + 4 + 2 = 6
  • Since 6 < 8, we can add 1 for a center character
  • Final answer: 6 + 1 = 7

The time complexity is O(n) for counting characters, and space complexity is O(k) where k is the number of unique characters.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with s = "aaBBccc":

Step 1: Count Character Frequencies

  • 'a' appears 2 times
  • 'B' appears 2 times
  • 'c' appears 3 times

Step 2: Calculate Pairs For each character, we determine how many can be used in pairs using v // 2 * 2:

  • 'a': 2 // 2 * 2 = 1 * 2 = 2 โ†’ Use all 2 characters
  • 'B': 2 // 2 * 2 = 1 * 2 = 2 โ†’ Use all 2 characters
  • 'c': 3 // 2 * 2 = 1 * 2 = 2 โ†’ Use 2 out of 3 characters

Total from pairs: 2 + 2 + 2 = 6

Step 3: Check for Center Character

  • Current palindrome length: 6
  • Original string length: 7
  • Since 6 < 7, we have leftover characters (the unused 'c')
  • We can place one character in the center: 6 + 1 = 7

Final Answer: 7

We could build palindromes like:

  • "aBccBa" with the extra 'c' in the center
  • "cBaaBc" with the extra 'c' in the center
  • "acBBca" with the extra 'c' in the center

The key insight: we used all even counts completely, used the even portion of odd counts, and added 1 for a center character since we had leftovers.

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def longestPalindrome(self, s: str) -> int:
5        # Count frequency of each character in the string
6        char_count = Counter(s)
7      
8        # Calculate the length of the longest palindrome
9        # For each character, we can use pairs (even count) in the palindrome
10        palindrome_length = sum(count // 2 * 2 for count in char_count.values())
11      
12        # If palindrome length is less than string length, we can add one odd character in the middle
13        # This checks if there's at least one character with odd frequency
14        if palindrome_length < len(s):
15            palindrome_length += 1
16      
17        return palindrome_length
18
1class Solution {
2    public int longestPalindrome(String s) {
3        // Array to store frequency of each character (ASCII range 0-127)
4        int[] charFrequency = new int[128];
5        int stringLength = s.length();
6      
7        // Count frequency of each character in the string
8        for (int i = 0; i < stringLength; i++) {
9            charFrequency[s.charAt(i)]++;
10        }
11      
12        // Calculate the length of longest palindrome
13        int palindromeLength = 0;
14      
15        // For each character, we can use pairs of it in the palindrome
16        // If a character appears 5 times, we can use 4 of them (2 pairs)
17        for (int frequency : charFrequency) {
18            // Add the largest even number less than or equal to frequency
19            palindromeLength += (frequency / 2) * 2;
20        }
21      
22        // If palindrome length is less than original string length,
23        // it means we have at least one character with odd frequency
24        // We can place one such character in the middle of palindrome
25        if (palindromeLength < stringLength) {
26            palindromeLength++;
27        }
28      
29        return palindromeLength;
30    }
31}
32
1class Solution {
2public:
3    int longestPalindrome(string s) {
4        // Array to store frequency of each ASCII character (128 possible values)
5        int charFrequency[128] = {0};
6      
7        // Count the frequency of each character in the string
8        for (char c : s) {
9            ++charFrequency[c];
10        }
11      
12        // Calculate the length of the longest palindrome
13        int palindromeLength = 0;
14      
15        // For each character, we can use pairs (even count) in the palindrome
16        // If a character appears 5 times, we can use 4 of them (2 pairs)
17        for (int frequency : charFrequency) {
18            palindromeLength += (frequency / 2) * 2;
19        }
20      
21        // If we haven't used all characters, we can add one more character in the middle
22        // This happens when there's at least one character with odd frequency
23        if (palindromeLength < s.size()) {
24            palindromeLength += 1;
25        }
26      
27        return palindromeLength;
28    }
29};
30
1/**
2 * Calculates the length of the longest palindrome that can be built with the given string characters
3 * @param s - Input string containing characters to build the palindrome
4 * @returns The length of the longest possible palindrome
5 */
6function longestPalindrome(s: string): number {
7    // Create a frequency map to count occurrences of each character
8    const characterFrequency: Record<string, number> = {};
9  
10    // Count the frequency of each character in the string
11    for (const character of s) {
12        characterFrequency[character] = (characterFrequency[character] || 0) + 1;
13    }
14  
15    // Calculate the length of the palindrome by taking pairs of characters
16    // For each character, we can use floor(count/2) * 2 characters in the palindrome
17    let palindromeLength = Object.values(characterFrequency).reduce(
18        (accumulator, frequency) => accumulator + Math.floor(frequency / 2) * 2, 
19        0
20    );
21  
22    // If there are any characters left (palindrome length < string length),
23    // we can place one odd character in the middle of the palindrome
24    palindromeLength += palindromeLength < s.length ? 1 : 0;
25  
26    return palindromeLength;
27}
28

Time and Space Complexity

The time complexity is O(n + |ฮฃ|), where n is the length of the string s and |ฮฃ| is the size of the character set. The Counter(s) operation takes O(n) time to count all characters in the string. The subsequent iteration through cnt.values() takes O(|ฮฃ|) time in the worst case when all possible characters appear in the string. Since the character set in this problem consists of ASCII characters, |ฮฃ| = 128.

The space complexity is O(|ฮฃ|). The Counter object stores at most |ฮฃ| unique characters and their frequencies, which requires O(|ฮฃ|) space. In this problem, |ฮฃ| = 128 for the ASCII character set.

Common Pitfalls

Pitfall 1: Misunderstanding the Center Character Logic

The Mistake: Many developers incorrectly try to count how many characters have odd frequencies and add that count to the result, thinking multiple odd-count characters can be used.

# INCORRECT approach
def longestPalindrome(self, s: str) -> int:
    char_count = Counter(s)
    palindrome_length = 0
    odd_count = 0
  
    for count in char_count.values():
        palindrome_length += count // 2 * 2
        if count % 2 == 1:
            odd_count += 1
  
    # Wrong: Adding all odd counts
    return palindrome_length + odd_count

Why It's Wrong: In a palindrome, only ONE character can have an odd count (placed in the center). The above code would incorrectly return 8 for "aabbcc" instead of 6.

The Fix: Add at most 1 to the result if ANY character has an odd count:

# CORRECT approach
if palindrome_length < len(s):
    palindrome_length += 1

Pitfall 2: Incorrectly Calculating Usable Pairs

The Mistake: Using only complete pairs and discarding the remainder entirely:

# INCORRECT approach
def longestPalindrome(self, s: str) -> int:
    char_count = Counter(s)
    palindrome_length = 0
  
    for count in char_count.values():
        palindrome_length += (count // 2) * 2  # Looks correct but...
  
    # Forgetting to check for center character
    return palindrome_length  # Missing the +1 for odd counts

Why It's Wrong: This approach forgets that if we have leftover characters after pairing, we can still use ONE of them as the center of the palindrome.

The Fix: Always check if the total paired length is less than the original string length:

palindrome_length = sum(count // 2 * 2 for count in char_count.values())
if palindrome_length < len(s):  # This check is crucial
    palindrome_length += 1

Pitfall 3: Over-complicating the Odd Count Check

The Mistake: Explicitly tracking whether any character has an odd count with additional variables:

# OVERLY COMPLEX approach
def longestPalindrome(self, s: str) -> int:
    char_count = Counter(s)
    palindrome_length = 0
    has_odd = False
  
    for count in char_count.values():
        palindrome_length += count // 2 * 2
        if count % 2 == 1:
            has_odd = True
  
    return palindrome_length + (1 if has_odd else 0)

Why It's Inefficient: While this works, it uses unnecessary variables and explicit modulo operations when a simpler comparison suffices.

The Fix: The condition palindrome_length < len(s) elegantly captures whether any odd-count character exists:

# Elegant one-liner after calculating pairs
return palindrome_length + int(palindrome_length < len(s))

This works because if all characters were used in pairs, palindrome_length would equal len(s). Any difference means odd-count characters exist.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More