Palindrome Pairs
HARDDescription
You are given a 0-indexed array of unique strings words.
A palindrome pair is a pair of integers (i, j) such that:
0 <= i, j < words.length,i != j, andwords[i] + words[j](the concatenation of the two strings) is a palindrome.
Return an array of all the palindrome pairs of words.
You must write an algorithm with O(sum of words[i].length) runtime complexity.
Example 1:
Input: words = ["abcd","dcba","lls","s","sssll"] Output: [[0,1],[1,0],[3,2],[2,4]] Explanation: The palindromes are ["abcddcba","dcbaabcd","slls","llssssll"]
Example 2:
Input: words = ["bat","tab","cat"] Output: [[0,1],[1,0]] Explanation: The palindromes are ["battab","tabbat"]
Example 3:
Input: words = ["a",""] Output: [[0,1],[1,0]] Explanation: The palindromes are ["a","a"]
Constraints:
1 <= words.length <= 50000 <= words[i].length <= 300words[i]consists of lowercase English letters.
Approaches
Checkout 4 different approaches to solve Palindrome Pairs. Click on different approaches to view the approach and algorithm in detail.
Using Hash Map
We can optimize the brute-force approach by avoiding the inner loop that iterates through all other words. Instead, for each word w1, we can deduce what its partner word w2 must look like. If w1 + w2 is a palindrome, w2 must be related to the reverse of w1. We can pre-process the words into a HashMap for quick lookups.
There are three main scenarios for a pair (w1, w2):
len(w1) == len(w2):w2must be the reverse ofw1.len(w1) > len(w2):w1must be of the formP + S, whereSis the reverse ofw2andPis a palindrome.len(w1) < len(w2):w2must be of the formS + P, whereSis the reverse ofw1andPis a palindrome.
We can iterate through each word and all its possible splits into a prefix and a suffix. If one part is a palindrome, we look for the reverse of the other part in the map.
Algorithm
- Create a
HashMapto store each word and its index. This allows for O(1) average time lookups. - Handle the edge case of an empty string. If
""exists inwords, find its index. Then, iterate through all other words; if a word is a palindrome, it can form a pair with the empty string in both orders. - Iterate through each word
wat indexiin thewordsarray. - For each word
w, iterate through all possible split pointsjfrom0tow.length(). - Split
wintoprefix = w.substring(0, j)andsuffix = w.substring(j). - Case 1: Check if the
prefixis a palindrome. If it is, reverse thesuffixto getreversedSuffix. Look forreversedSuffixin theHashMap. If it exists at an indexkdifferent fromi, then(k, i)is a palindrome pair (words[k] + words[i]forms a palindrome). - Case 2: Check if the
suffixis a palindrome. If it is, reverse theprefixto getreversedPrefix. Look forreversedPrefixin theHashMap. If it exists at an indexkdifferent fromi, then(i, k)is a palindrome pair. Note: Whenj=0, the prefix is empty, and this case becomes checking for the reverse of the whole word. To avoid duplicates when the suffix is empty (j = w.length()), we can add a conditionsuffix.length() > 0or use aSetfor the results.
This approach improves upon brute force by intelligently searching for the second word in a pair. By pre-populating a HashMap with all words and their indices, we can quickly check for the existence of a required word (e.g., the reverse of a suffix). The main logic involves iterating through each word and, for each word, iterating through all possible split points. This creates a prefix and a suffix. We check if either part is a palindrome. If the prefix is a palindrome, we need a word that is the reverse of the suffix to place before our current word. If the suffix is a palindrome, we need a word that is the reverse of the prefix to place after our current word.
class Solution {
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> result = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < words.length; i++) {
map.put(words[i], i);
}
for (int i = 0; i < words.length; i++) {
String word = words[i];
for (int j = 0; j <= word.length(); j++) {
String prefix = word.substring(0, j);
String suffix = word.substring(j);
// Case 1: prefix is a palindrome, look for reversed suffix
if (isPalindrome(prefix)) {
String reversedSuffix = new StringBuilder(suffix).reverse().toString();
if (map.containsKey(reversedSuffix) && map.get(reversedSuffix) != i) {
result.add(Arrays.asList(map.get(reversedSuffix), i));
}
}
// Case 2: suffix is a palindrome, look for reversed prefix
// suffix.length() != 0 to avoid duplicates when word is a palindrome
// (e.g. "aba", prefix="aba", suffix=""). This is handled by Case 1 when j=0.
if (isPalindrome(suffix) && suffix.length() != 0) {
String reversedPrefix = new StringBuilder(prefix).reverse().toString();
if (map.containsKey(reversedPrefix) && map.get(reversedPrefix) != i) {
result.add(Arrays.asList(i, map.get(reversedPrefix)));
}
}
}
}
return result;
}
private boolean isPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s.charAt(left++) != s.charAt(right--)) {
return false;
}
}
return true;
}
}
Complexity Analysis
Pros and Cons
- Significantly faster than the brute-force approach for N > K.
- Avoids the O(N^2) pair iteration.
- The time complexity of O(N * K^2) might still be too slow for the given constraints.
- Requires careful handling of edge cases like empty strings and duplicate pairs.
Code Solutions
Checking out 3 solutions in different languages for Palindrome Pairs. Click on different languages to view the code.
using System.Collections.Generic ; using System.Linq ; public class Solution { public IList < IList < int >> PalindromePairs ( string [] words ) { var results = new List < IList < int >>(); var reverseDict = words . Select (( w , i ) => new { Word = w , Index = i }). ToDictionary ( w => new string ( w . Word . Reverse (). ToArray ()), w => w . Index ); for ( var i = 0 ; i < words . Length ; ++ i ) { var word = words [ i ]; for ( var j = 0 ; j <= word . Length ; ++ j ) { if ( j > 0 && IsPalindrome ( word , 0 , j - 1 )) { var suffix = word . Substring ( j ); int pairIndex ; if ( reverseDict . TryGetValue ( suffix , out pairIndex ) && i != pairIndex ) { results . Add ( new [] { pairIndex , i }); } } if ( IsPalindrome ( word , j , word . Length - 1 )) { var prefix = word . Substring ( 0 , j ); int pairIndex ; if ( reverseDict . TryGetValue ( prefix , out pairIndex ) && i != pairIndex ) { results . Add ( new [] { i , pairIndex }); } } } } return results ; } private bool IsPalindrome ( string word , int startIndex , int endIndex ) { var i = startIndex ; var j = endIndex ; while ( i < j ) { if ( word [ i ] != word [ j ]) return false ; ++ i ; -- j ; } return true ; } }Video Solution
Watch the video walkthrough for Palindrome Pairs
Similar Questions
5 related questions you might find useful
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.