Skip to Content

Palindrome Pairs

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, and

words[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.

There are multiple valid approaches but using a trie data structure is the simplest and most general option. Learning this will help with many difficult problems.

class Trie {
    Integer endIndex;
    Map<Character, Trie> child = new HashMap<>();
    List<Integer> palindromesFromHere = new ArrayList<>();

    boolean isWordNode() {
        return endIndex != null;
    }
}

class Solution {

    private Trie buildTrie(String[] words) {
        final Trie root = new Trie();
        for (int i = 0, n = words.length; i < n; i++) {
            final String currWord = words[i];
            Trie currentTrie = root;
            for (int j = 0, jn = currWord.length(); j < jn; j++) {
                final int reverseIndex = jn - j - 1;
                if (isPalindrome(currWord, 0, reverseIndex)) currentTrie.palindromesFromHere.add(i);
                final Character currReverseChar = currWord.charAt(reverseIndex);
                currentTrie = currentTrie.child.computeIfAbsent(currReverseChar, c -> new Trie());
            }
            currentTrie.endIndex = i;
        }
        return root;
    }

    public List<List<Integer>> palindromePairs(String[] words) {
        final Trie root = buildTrie(words);
        final List<List<Integer>> pairs = new ArrayList<>();

        outer: for (int i = 0, n = words.length; i < n; i++) {
            final String currWord = words[i];
            Trie currentTrie = root;
            for (int j = 0, jn = currWord.length(); j < jn; j++) {
                // case 3 : When W1 is bigger than W2 and W1 ends with palindrome
                if (currentTrie.isWordNode() && isPalindrome(currWord, j, jn - 1)) {
                    pairs.add(Arrays.asList(i, currentTrie.endIndex));
                }

                // move to next trie
                currentTrie = currentTrie.child.get(currWord.charAt(j));
                if (currentTrie == null) continue outer;
            }

            // case 1 : W1 and W2 are of same size and reverse of each other
            if (currentTrie.isWordNode() && currentTrie.endIndex != i) {
                pairs.add(Arrays.asList(i, currentTrie.endIndex));
            }

            // case 2 : W1 is smaller than W2 and W2 begins with palindrome

            for (final Integer palindromeIndex : currentTrie.palindromesFromHere) {
                pairs.add(Arrays.asList(i, palindromeIndex));
            }
        }

        return pairs;
    }

    private boolean isPalindrome(String word, int fromIndex, int toIndex) {
        for (int i = fromIndex, j = toIndex; i < j; i++, j--) {
            if (word.charAt(i) != word.charAt(j)) return false;
        }

        return true;
    }
}

Posted by Jamie Meyer 14 days ago