Leetcode 336. Given a list of unique words. Find all pairs of distinct indices (i, j)
in the given list, so that the concatenation of the two words, i.e. words[i] + words[j]
is a palindrome.
Example 1:
Given words
= ["bat", "tab", "cat"]
Return [[0, 1], [1, 0]]
The palindromes are ["battab", "tabbat"]
Example 2:
Given words
= ["abcd", "dcba", "lls", "s", "sssll"]
Return [[0, 1], [1, 0], [3, 2], [2, 4]]
The palindromes are ["dcbaabcd", "abcddcba", "slls", "llssssll"]
Solution. The solution is from here. We uses trie. For each string, we save it trie in reverse order. For example, if it is abc. The trie will be like below. a has value pos. This tells that trie cba is a word.
When we use cbaxyx to match the trie, it will finally goes to trie a. And when it sees a has pos, we check if xyx is a palindrome. If it is, then we know cbaxyx and abc is a pair of palindrome.
Think about words[] = {“cbaaa”, “bc”, “abc”}. The trie will be like:
When it tries to match cbaaa, when it arrives cb, its pos is 1, then we should check if aaa is a palindrome. When it arrives cba, we should check if rest of string aa is a palindrome. In this way, we can know that [“cbaaa”, “bc”], [“cbaaa”, “abc”] are pairs.
How about [“xyxcba”, “abc”, “xcba”]?
When we check abc and it arrives c in trie. We should know that the rest of string after abc has palindrome. So we modify trie like this:
c has a list palins. It tells string below c, what rest of word is palindrome. When we scan abc, it will finally stops at c in xyxabc. When it ends, check if palins list of trie c is not empty. If not, it may be able to concatenate into palindrome.
We can do it in another way by rolling hash which I wrote a post before. But the code is too much overhead.
Below is my code:
public static class Trie { int pos; Trie[] nodes; // consider xyxabc. if current trie is 'a'. Then a.nodes has information. It means string after a is palindrome List<Integer> palins; public Trie() { pos = -1; nodes = new Trie[26]; palins = new ArrayList<>(); } } public static void add(Trie root, String word, int pos) { for (int i = word.length() - 1; i >= 0; i--) { char ch = word.charAt(i); if (isPalindrome(word, 0, i)) { // check if substring(0, i) is palindrome. root.palins.add(pos); } if (root.nodes[ch - 'a'] == null) { root.nodes[ch - 'a'] = new Trie(); } root = root.nodes[ch - 'a']; } root.pos = pos; // if it is xyxcba. Until now, the node should be at x. root.palins.add(pos); } public static void search(Trie root, String[] words, int i, List<List<Integer>> ans) { int len = words[i].length(); for (int j = 0; j < len && root != null; j++) { if (root.pos >= 0 && i != root.pos && isPalindrome(words[i], j, len - 1)) { ans.add(Arrays.asList(new Integer[] {i, root.pos})); } char ch = words[i].charAt(j); root = root.nodes[ch - 'a']; } if (root != null && root.palins.size() > 0) { // assume 'xyxabc' is in trie, now try 'cba' for (int j : root.palins) { if (j != i) { ans.add(Arrays.asList(new Integer[] {i, j})); } } } } public static List<List<Integer>> palindromePairs(String[] words) { List<List<Integer>> ans = new ArrayList<>(); Trie trie = new Trie(); for (int i = 0; i < words.length; i++) { add(trie, words[i], i); } for (int i = 0; i < words.length; i++) { search(trie, words, i, ans); } return ans; } public static boolean isPalindrome(String str, int i, int j) { while (i < j) { if (str.charAt(i++) != str.charAt(j--)) { return false; } } return true; }
Check my code on github.
Pingback: Palindromic pairs in an array of words - Tutorial Guruji()