/**
* 336. Palindrome Pairs
* Given a list of unique words, return all the pairs of the distinct indices (i, j) in the given list,
* so that the concatenation of the two words words[i] + words[j] is a palindrome.
* Example 1:
* Input: words = [“abcd”,”dcba”,”lls”,”s”,”sssll”]
* Output: [[0,1],[1,0],[3,2],[2,4]]
* Explanation: The palindromes are [“dcbaabcd”,”abcddcba”,”slls”,”llssssll”]
*/
See solution to the problem on github here: https://github.com/zcoderz/leetcode/blob/main/src/main/java/trie/PalindromePairs.java
This problem has a unique and very elegant approach. Hence, have included this question here.
Conceptually understanding the problem:
- Problem is asking for palindrome pairs between words
- The question involves string matching and traversal so trie data structure can be used here
- A palindrome between two words occurs when one string matches the reverse of other, so we can add the words into a trie in reverse order
- Once the trie has been built we can iterate through trie to find relevant matches
Cases in which a palindrome can occur:
- Word matches trie exactly and thus a one-to-one palindrome match occurs
- The trie node contains another and the given word being iterated forms a palindrome via the remaining characters
- Word has been fully exhausted but trie has one or more palindromes left
Here is the algorithm:
- First build the trie using the words in their reverse order
- At each node in the trie if the sub word forms a palindrome, record that in the trie node
- For each word
- iterate the characters while traversing the trie.
- Handle the case where trie node contains a given word and the remaining characters in the word being iterated form a palindrome. This is the case 2 above.
- if the last node in trie contains another word once the original word has been completely iterated over then an exact palindrome match occurred. This is case 1 above
- after the complete word has been processed, if the trie node contains on or more palindromes then they must be accounted
- iterate the characters while traversing the trie.
Here is the code:
void processPalindrome() {
for (int i = 0; i < words.length; i++) {
String word = words[i];
Trie node = root;
for (int j = 0; j < word.length(); j++) {
if ((node.wordId != -1) && isPalindrome(word.substring(j))) {
//word has a palindrome left after match : case 2
List<Integer> item = new ArrayList<>();
item.add(i);
item.add(node.wordId);
results.add(item);
}
Character ch = word.charAt(j);
node = node.child.get(ch);
if (node == null) break;
}
if (node == null) {
continue;
}
if (node.wordId != -1 && node.wordId != i) {
//word had an exact palindrome match : case 1
List<Integer> item = new ArrayList<>();
item.add(i);
item.add(node.wordId);
results.add(item);
}
for (Integer k : node.palindromes) {
//tree has one or more palindromes left : case 3
List<Integer> item = new ArrayList<>();
item.add(i);
item.add(k);
results.add(item);
}
}
}
void buildTrie() {
for (int i = 0; i < words.length; i++) {
String word = words[i];
//add items in reverse to trie so that while walking the tree you match in reverse
//to identify palindrome
word = new StringBuilder(word).reverse().toString();
Trie node = root;
for (int j = 0; j < word.length(); j++) {
//identify that there is a remaining palindrome after this node
if (isPalindrome(word.substring(j))) {
node.palindromes.add(i);
}
Character ch = word.charAt(j);
node = node.child.computeIfAbsent(ch, k -> new Trie());
}
node.wordId = i;
}
}