Palindrome Pairs

By | March 15, 2016
Share the joy
  •  
  •  
  •  
  •  
  •  
  •  

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.
palinpair1

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:
palinpair2
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”]?
palinpair3

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:
palinpair4
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.

  • Chandra Pinjala

    Perfect Explanation!! Thank You so much for your explanation. This helped me understand.

  • Cheng-Liang Yu

    wow this is really an Legendary explanation.
    Thank you so much.
    I logged in to write this comment since I really want to type this and thank you.
    I was confused about this question for so long.

  • Lokesh sanapalli

    Beautifully explained! I was wondering if somebody could have taken perfect examples and explained. You did a very good job. Thanks :)

  • Lokesh sanapalli

    Hi,

    It seems there’s a typo in this line

    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.

    it should be xyxcba not xyxabc I thinnk ?

  • Lokesh sanapalli

    Hi,

    In the last image, I did not understand, why ‘c’ has palins or [2,0]. It should be at ‘x’ right ? because we’re checking from 0 to i, ‘xyxc’ is not a palindrome. I am confused here, please help!

  • Pingback: Palindromic pairs in an array of words - Tutorial Guruji()

  • Alberto Rodriguez

    It’s brilliant explanation.