Talentd
FleetCode
Make and follow sheets.Sign in

Longest Palindrome by Concatenating Two Letter Words solution and explanation

MediumArrayCountingGreedyHash Table7 min read53.5% acceptanceUpdated 9 Apr 2026
Longest Palindrome by Concatenating Two Letter Words | Simple Way | Leetcode 2131 | codestorywithMIKcodestorywithMIK · 17:35 · 9K viewsWatch on YouTube
9 more videos on this problem
  1. Longest Palindrome by Concatenating Two Letter Words | Simple Way | Leetcode 2131 | codestorywithMIKcodestorywithMIK · 17:35 · 9K views
  2. 2131. Longest Palindrome by Concatenating Two Letter Words (Leetcode Medium)Programming Live with Larry · 13:18 · 1K views
  3. Longest Palindrome by Concatenating Two Letter Words | Leetcode 2131Techdose · 23:32 · 1K views
  4. 2131. Longest Palindrome by Concatenating Two Letter Words || DSA || Leetcode Daily Challenge ||Leetcode Daily Challenge · 8:34 · 1K views
  5. Longest Palindrome by Concatenating Two Letter Words - Leetcode 2131 - PythonTim Huang · 5:14 · 733 views
  6. Explaining Leetcode - #2131 Longest Palindrome by Concatenating Two Letter Words (Medium)JTECH · 22:20 · 619 views
  7. 2131. Longest Palindrome by Concatenating Two Letter Words | Leetcode Daily - PythonLeetcode Daily · 6:25 · 515 views
  8. 2131. Longest Palindrome by Concatenating Two Letter Words | JavaScript | LeetCode Daily ChallengeLeetCodeWithMonu · 14:21 · 347 views
  9. 2131. Longest Palindrome by Concatenating Two Letter Words || Leetcode || C++Code with Sky · 12:22 · 263 views
  10. 2131. Longest Palindrome by Concatenating Two Letter Words | Leetcode | PythonJobless Engineer · 10:21 · 233 views

The problem

You are given an array of strings words. Each element of words consists of two lowercase English letters.

Create the longest possible palindrome by selecting some elements from words and concatenating them in any order. Each element can be selected at most once.

Return the length of the longest palindrome that you can create. If it is impossible to create any palindrome, return 0.

A palindrome is a string that reads the same forward and backward.

Example 1:

Input: words = ["lc","cl","gg"]
Output: 6
Explanation: One longest palindrome is "lc" + "gg" + "cl" = "lcggcl", of length 6.
Note that "clgglc" is another longest palindrome that can be created.

Example 2:

Input: words = ["ab","ty","yt","lc","cl","ab"]
Output: 8
Explanation: One longest palindrome is "ty" + "lc" + "cl" + "yt" = "tylcclyt", of length 8.
Note that "lcyttycl" is another longest palindrome that can be created.

Example 3:

Input: words = ["cc","ll","xx"]
Output: 2
Explanation: One longest palindrome is "cc", of length 2.
Note that "ll" is another longest palindrome that can be created, and so is "xx".

Constraints:

  • 1 <= words.length <= 105
  • words[i].length == 2
  • words[i] consists of lowercase English letters.

Approach overview

Problem Overview: You receive an array of two‑letter strings. You may concatenate selected words in any order, but each word can be used once. The goal is to build the longest possible palindrome string. Because every word has exactly two characters, the structure of the palindrome depends on pairing words with their reversed counterpart.

Approach 1: Greedy Pair Matching (O(n) time, O(n) space)

This approach counts how many times each two‑letter word appears using a hash map. While iterating through the array, check whether the reverse of the current word already exists in the map. If it does, you form a palindrome segment of length 4 (for example "ab" + "ba"). Decrease the stored count and add 4 to the result. Words where both characters are the same (like "aa" or "cc") require special handling: they can pair with another identical word to contribute 4 characters, but a single leftover symmetric word can sit in the middle of the palindrome and contribute 2 characters.

The greedy idea works because pairing reversed words always increases the palindrome length without affecting other combinations. A frequency map guarantees O(1) lookups while scanning the array once. This method relies heavily on hash table counting and a small amount of greedy reasoning to place pairs and optionally one center element.

Approach 2: String Reversal and Hash (O(n) time, O(n) space)

Another perspective is to treat every word and explicitly check its reversed form. Insert each word into a hash map that tracks unused occurrences. For a given word w, compute reverse(w). If the reversed word exists in the map with available frequency, pair them and increase the palindrome length by 4. Otherwise, store the current word for a potential future match.

Symmetric words like "gg" are handled by pairing identical entries from the map. After processing all words, if any symmetric word still has a remaining count, one of them can be placed in the center of the palindrome, contributing 2 characters. The algorithm is still a single pass with constant‑time hash operations. It highlights the importance of fast reverse lookups using string manipulation and frequency counting.

Recommended for interviews: The greedy pair matching solution is what most interviewers expect. It demonstrates that you recognize the palindrome structure and exploit the fixed two‑character word length. Showing the counting logic first proves you understand the pairing rules, while the optimized hash map implementation confirms you can reduce the problem to an O(n) scan using efficient lookups.

Approach 1: Greedy Pair Matching

The first approach involves counting occurrences of each word and its reverse. If a word appears with its reverse, they can be directly paired to contribute to the palindrome length. Symmetrical words like 'gg' can be used optimally to form palindromes, with one potentially serving as a center if their count is odd.

from collections import Counter

class Solution:
    def longestPalindrome(self, words):
        count = Counter(words)
        length = 0
        middle_used = False

        for word, cnt in count.items():
            rev = word[::-1]
            if word == rev:  # Symmetric word like 'gg'
                length += (cnt // 2) * 4
                if cnt % 2 == 1 and not middle_used:
                    length += 2
                    middle_used = True
            elif rev in count:
                pairs = min(cnt, count[rev])
                length += pairs * 4
                count[rev] = 0  # Mark reverse used

        return length

# Example usage:
sol = Solution()
words = ["lc", "cl", "gg"]
print(sol.longestPalindrome(words))
import java.util.HashMap;

class Solution {
    public int longestPalindrome(String[] words) {
        HashMap<String, Integer> map = new HashMap<>();
        int length = 0;
        boolean midUsed = false;

        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }

        for (String word : map.keySet()) {
            int count = map.get(word);
            String reverse = new StringBuilder(word).reverse().toString();

            if (word.equals(reverse)) { // Symmetrical
                length += (count / 2) * 4;
                if (count % 2 == 1 && !midUsed) {
                    length += 2;
                    midUsed = true;
                }
            } else if (map.containsKey(reverse)) {
                int minPairs = Math.min(count, map.get(reverse));
                length += minPairs * 4;
                map.put(reverse, 0); // Mark reverse used
            }
        }

        return length;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        String[] words = {"lc", "cl", "gg"};
        System.out.println(sol.longestPalindrome(words));
    }
}
#include <iostream>
#include <unordered_map>
using namespace std;

int longestPalindrome(vector<string>& words) {
    unordered_map<string, int> freq;
    int length = 0, middleUsed = 0;

    // Fill the map with word counts
    for (const auto& word : words) {
        freq[word]++;
    }

    for (auto& [word, count] : freq) {
        string rev = string(1, word[1]) + word[0];
        if (word == rev) {  // Self-mirrored like "gg"
            length += (count / 2) * 4;
            if (count % 2 == 1 && !middleUsed) {
                length += 2;
                middleUsed = 1;
            }
        } else if (freq.find(rev) != freq.end()) {
            int pairs = min(count, freq[rev]);
            length += pairs * 4;
            freq[rev] = 0;  // Mark used
        }
    }

    return length;
}

int main() {
    vector<string> words = {"lc", "cl", "gg"};
    cout << longestPalindrome(words) << endl;
    return 0;
}
function longestPalindrome(words) {
    const count = {};
    let length = 0;
    let middleUsed = false;

    for (let word of words) {
        count[word] = (count[word] || 0) + 1;
    }

    for (let word of Object.keys(count)) {
        let rev = word[1] + word[0];
        if (word === rev) { // Symmetric word
            length += Math.floor(count[word] / 2) * 4;
            if (count[word] % 2 === 1 && !middleUsed) {
                length += 2;
                middleUsed = true;
            }
        } else if (count[rev]) {
            let pairs = Math.min(count[word], count[rev]);
            length += pairs * 4;
            delete count[rev]; // Mark reverse used
        }
    }

    return length;
}

// Example usage:
const words = ["lc", "cl", "gg"];
console.log(longestPalindrome(words));
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_WORDS 100000

typedef struct {
    char word[3];
    int count;
} WordCount;

// Comparator for qsort
int cmp(const void *a, const void *b) {
    return strcmp(((WordCount *)a)->word, ((WordCount *)b)->word);
}

int longestPalindrome(char **words, int wordsSize) {
    WordCount map[MAX_WORDS];
    int mapSize = 0;
    int length = 0, middleUsed = 0;

    // Fill the map with word counts
    for (int i = 0; i < wordsSize; ++i) {
        int j;
        for (j = 0; j < mapSize; ++j) {
            if (strcmp(map[j].word, words[i]) == 0) {
                map[j].count++;
                break;
            }
        }
        if (j == mapSize) {
            strcpy(map[mapSize].word, words[i]);
            map[mapSize++].count = 1;
        }
    }

    qsort(map, mapSize, sizeof(WordCount), cmp);

    for (int i = 0; i < mapSize; ++i) {
        char reversed[3];
        reversed[0] = map[i].word[1];
        reversed[1] = map[i].word[0];
        reversed[2] = '\0';
        
        if (strcmp(map[i].word, reversed) == 0) {  // cases like "gg"
            length += (map[i].count / 2) * 4;
            if (map[i].count % 2 != 0 && !middleUsed) {
                length += 2;
                middleUsed = 1;
            }
        } else {
            for (int j = i + 1; j < mapSize; ++j) {
                if (strcmp(map[j].word, reversed) == 0) {
                    length += 4 * (map[i].count < map[j].count ? map[i].count : map[j].count);
                    map[j].count = 0;  // Mark used
                    break;
                }
            }
        }
    }

    return length;
}

int main() {
    char *words[] = {"lc", "cl", "gg"};
    int wordsSize = 3;
    printf("%d", longestPalindrome(words, wordsSize));
    return 0;
}
using System;
using System.Collections.Generic;

public class Solution {
    public int LongestPalindrome(string[] words) {
        var dict = new Dictionary<string, int>();
        int length = 0;
        bool middleUsed = false;

        foreach (var word in words) {
            if (!dict.ContainsKey(word)) {
                dict[word] = 0;
            }
            dict[word]++;
        }

        foreach (var kv in dict) {
            string word = kv.Key;
            int count = kv.Value;
            string revWord = new string(new char[] { word[1], word[0] });

            if (word == revWord) { // Symmetric word
                length += (count / 2) * 4;
                if (count % 2 == 1 && !middleUsed) {
                    length += 2;
                    middleUsed = true;
                }
            } else if (dict.ContainsKey(revWord)) {
                int pairs = Math.Min(count, dict[revWord]);
                length += pairs * 4;
                dict[revWord] = 0; // Mark reverse used
            }
        }
        return length;
    }

    public static void Main(string[] args) {
        Solution sol = new Solution();
        string[] words = new string[] { "lc", "cl", "gg" };
        Console.WriteLine(sol.LongestPalindrome(words));
    }
}

The Python solution leverages the `Counter` class for counting words efficiently. It checks each word and its reverse to determine the optimal contributions to the possible palindrome formation. Special attention is given to self-reversing words like 'gg' for potential middle use.

Complexity

Time Complexity: O(n) due to counter operations.
Space Complexity: O(n) for the Counter dictionary.

Try this approach in the editor

Approach 2: String Reversal and Hash

This approach involves creating a hash map to quickly check for the existence of a word's reverse in the input, allowing us to efficiently pair words or use symmetric words optimally. We calculate pairs and handle middle contributions by taking account of unsused symmetric words.

class Solution:
    def longestPalindrome(self, words):
        count = [[0] * 26 for _ in range(26)]
        length = 0
        middle_used = False

        for word in words:
            a, b = ord(word[0]) - ord('a'), ord(word[1]) - ord('a')
            if count[b][a] > 0:
                length += 4
                count[b][a] -= 1
            else:
                count[a][b] += 1

        for i in range(26):
            if count[i][i] > 0:
                middle_used = True
                break

        return length + (2 if middle_used else 0)

# Example usage:
sol = Solution()
words = ["lc", "cl", "gg"]
print(sol.longestPalindrome(words))
class Solution {
    public int longestPalindrome(String[] words) {
        int[][] map = new int[26][26];
        int length = 0;
        boolean hasMiddle = false;

        for (String word : words) {
            int a = word.charAt(0) - 'a';
            int b = word.charAt(1) - 'a';

            if (map[b][a] > 0) {
                length += 4;
                map[b][a]--;
            } else {
                map[a][b]++;
            }
        }

        for (int i = 0; i < 26; ++i) {
            if (map[i][i] > 0) {
                hasMiddle = true;
                break;
            }
        }

        return length + (hasMiddle ? 2 : 0);
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        String[] words = {"lc", "cl", "gg"};
        System.out.println(sol.longestPalindrome(words));
    }
}
#include <iostream>
#include <vector>
using namespace std;

int longestPalindrome(vector<string>& words) {
    int map[26][26] = {0};
    int length = 0;
    int symmetricCount = 0;

    for (const string& word : words) {
        int first = word[0] - 'a';
        int second = word[1] - 'a';
        
        if (map[second][first] > 0) {
            length += 4;
            map[second][first]--;
        } else {
            map[first][second]++;
        }
    }

    for (int i = 0; i < 26; ++i) {
        if (map[i][i] > 0) {
            symmetricCount = 1;
            break;
        }
    }

    return length + symmetricCount * 2;
}

int main() {
    vector<string> words = {"lc", "cl", "gg"};
    cout << longestPalindrome(words) << endl;
    return 0;
}
function longestPalindrome(words) {
    const map = Array.from({length: 26}, () => Array(26).fill(0));
    let length = 0;
    let middleUsed = false;

    for (const word of words) {
        const a = word.charCodeAt(0) - 97;
        const b = word.charCodeAt(1) - 97;

        if (map[b][a] > 0) {
            length += 4;
            map[b][a]--;
        } else {
            map[a][b]++;
        }
    }

    for (let i = 0; i < 26; i++) {
        if (map[i][i] > 0) {
            middleUsed = true;
            break;
        }
    }

    return length + (middleUsed ? 2 : 0);
}

// Example usage:
const words = ["lc", "cl", "gg"];
console.log(longestPalindrome(words));
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int longestPalindrome(char** words, int wordsSize) {
    int map[26][26] = {0};
    int length = 0;
    int symmetricCount = 0;

    for (int i = 0; i < wordsSize; ++i) {
        int first = words[i][0] - 'a';
        int second = words[i][1] - 'a';
        
        if (map[second][first] > 0) {
            length += 4;
            map[second][first]--;
        } else {
            map[first][second]++;
        }
    }

    for (int i = 0; i < 26; ++i) {
        if (map[i][i] > 0) {
            symmetricCount = 1;
            break;
        }
    }

    return length + symmetricCount * 2;
}

int main() {
    char* words[] = { "lc", "cl", "gg" };
    int wordsSize = sizeof(words) / sizeof(words[0]);
    printf("%d", longestPalindrome(words, wordsSize));
    return 0;
}
using System;

public class Solution {
    public int LongestPalindrome(string[] words) {
        int[,] map = new int[26, 26];
        int length = 0;
        bool hasMiddle = false;

        foreach (var word in words) {
            int a = word[0] - 'a';
            int b = word[1] - 'a';

            if (map[b, a] > 0) {
                length += 4;
                map[b, a]--;
            } else {
                map[a, b]++;
            }
        }

        for (int i = 0; i < 26; ++i) {
            if (map[i, i] > 0) {
                hasMiddle = true;
                break;
            }
        }

        return length + (hasMiddle ? 2 : 0);
    }

    public static void Main(string[] args) {
        Solution sol = new Solution();
        string[] words = { "lc", "cl", "gg" };
        Console.WriteLine(sol.LongestPalindrome(words));
    }
}

In Python, symbol counting across combined alphabets supports identifying palindrome pairs, with a focus on O(1) checks in the two-dimensional count list. It detects symmetry with conditional logic for central placement.

Complexity

Time Complexity: O(n), list operations are efficiently managed.
Space Complexity: O(1), list size is bounded to a fixed 26x26.

Try this approach in the editor

Approach 3: Greedy + Hash Table

First, we use a hash table cnt to count the occurrences of each word.

Iterate through each word k and its count v in cnt:

  • If the two letters in k are the same, we can concatenate \left \lfloor \frac{v}{2} \right \rfloor times 2 copies of k to the front and back of the palindrome. If there is one k left, we can record it in x for now.

  • If the two letters in k are different, we need to find a word k' such that the two letters in k' are the reverse of k, i.e., k' = k[1] + k[0]. If k' exists, we can concatenate min(v, cnt[k']) copies of k to the front and back of the palindrome.

After the iteration, if x is not empty, we can also place one word in the middle of the palindrome.

The time complexity is O(n), and the space complexity is O(n), where n is the number of words.

function longestPalindrome(words: string[]): number {
    const cnt = new Map<string, number>();
    for (const w of words) cnt.set(w, (cnt.get(w) || 0) + 1);
    let [ans, x] = [0, 0];
    for (const [k, v] of cnt.entries()) {
        if (k[0] === k[1]) {
            x += v & 1;
            ans += Math.floor(v / 2) * 2 * 2;
        } else {
            ans += Math.min(v, cnt.get(k[1] + k[0]) || 0) * 2;
        }
    }
    ans += x ? 2 : 0;
    return ans;
}
func longestPalindrome(words []string) int {
	cnt := map[string]int{}
	for _, w := range words {
		cnt[w]++
	}
	ans, x := 0, 0
	for k, v := range cnt {
		if k[0] == k[1] {
			x += v & 1
			ans += v / 2 * 2 * 2
		} else {
			rk := string([]byte{k[1], k[0]})
			if y, ok := cnt[rk]; ok {
				ans += min(v, y) * 2
			}
		}
	}
	if x > 0 {
		ans += 2
	}
	return ans
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Greedy Pair Matching

Time Complexity: O(n) due to counter operations.
Space Complexity: O(n) for the Counter dictionary.

String Reversal and Hash

Time Complexity: O(n), list operations are efficiently managed.
Space Complexity: O(1), list size is bounded to a fixed 26x26.

Greedy + Hash Table–

Complexity analysis

ApproachTimeSpaceWhen to Use
Greedy Pair Matching with Frequency MapO(n)O(n)Best general solution. Single pass with hash counting and greedy pairing.
String Reversal and Hash LookupO(n)O(n)Good when emphasizing reverse string matching and explicit pair detection.

Frequently asked questions

Is Longest Palindrome by Concatenating Two Letter Words easy or hard?

The problem is rated Medium because the idea is simple but requires careful handling of symmetric words like "aa". Candidates must recognize that reversed pairs build the palindrome edges while at most one identical pair can occupy the center.

Longest Palindrome by Concatenating Two Letter Words Python/Java solution

Python, Java, C++, and JavaScript implementations typically use a dictionary or HashMap to track counts. During iteration, the algorithm checks the reversed string and updates the palindrome length accordingly. The final complexity remains O(n) time and O(n) space across languages.

How to solve Longest Palindrome by Concatenating Two Letter Words in O(n)?

Store counts of each two‑letter word in a hash map. For every word, check if its reversed version already exists. If it does, form a pair and add 4 to the palindrome length. After processing all words, check if one symmetric word like "aa" remains to place in the center, adding 2 more characters.

What is the best approach for Longest Palindrome by Concatenating Two Letter Words?

The optimal approach uses greedy pair matching with a hash map. Count occurrences of each two‑letter word and match it with its reversed counterpart (for example "ab" with "ba"). Each pair contributes 4 characters to the palindrome. Words with identical characters like "aa" can form pairs or act as a single center element.

Is Longest Palindrome by Concatenating Two Letter Words asked at Google/Amazon/Meta?

Palindrome construction and hash‑based pairing problems appear frequently in interviews at companies like Google, Amazon, and Meta. Variations of this question test string manipulation, greedy reasoning, and efficient hash table usage.

What data structure is used in Longest Palindrome by Concatenating Two Letter Words?

The core data structure is a hash map (or dictionary) used to count word frequencies and perform O(1) reverse lookups. This allows quick pairing between a word and its reversed counterpart while scanning the array.

What is the time complexity of Longest Palindrome by Concatenating Two Letter Words?

The optimal solution runs in O(n) time where n is the number of words. Each word is processed once, and hash map lookups for reversed strings take O(1) on average. Space complexity is O(n) to store word frequencies.

Comments

Now write it yourselfClose this page and solve Longest Palindrome by Concatenating Two Letter Words in the editor, without looking.
Practise Longest Palindrome by Concatenating Two Letter Words