# 336 Palindrome Pairs

## 336. [Palindrome Pairs](https://leetcode.com/problems/palindrome-pairs/description/)

## 1. Question

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"]`

## 2. Implementation

**(1) Trie**

思路:

(1) 要看两个string连在一起是否回文串，那么这两个string必然满足这样的性质：一个string的前缀和另一个string的后缀相同且除去前(后)缀的剩余是回文串(包括空字符串)。

(2)为了比较不同string的前后缀，我们可以利用trie，插入时将每个word按照倒序放入trie，这样的好处是，当我们查询一个string的前缀时，可以通过trie快速的找到有没与前缀相同的后缀

(3)对于子串中存在回文串的string(包括空字符串), 比如abac中有aba这个回文子串，我们在trieNode里维护一个list，保存这种特性的word在输入数组的index

```java
class Solution {
    class TrieNode {
        //记录word在输入数组的index， 预设值为-1
        int index; 
        // 保存word在输入数组的index，这种word满足条件：
        // word的后缀在trie里, 剩余部分是回文串(包括空串)。比如abac, c为后缀，剩余部分aba。注意abac是倒着插入(即caba)trie的
        List<Integer> list; 
        TrieNode[] childNode;

        public TrieNode() {
            index = -1; 
            list = new ArrayList<>();
            childNode = new TrieNode[26];
        }
    }

    class Trie {
        TrieNode root;

        public Trie() {
            root = new TrieNode();
        }

        // 将Word倒序的插入Trie里
        public void insert(String word, int index) {
            TrieNode curNode = root;

            for (int i = word.length() - 1; i >= 0; i--) {
                if (isPalindrome(word, 0, i)) {
                    curNode.list.add(index);
                }

                int pos = word.charAt(i) - 'a';

                if (curNode.childNode[pos] == null) {
                    curNode.childNode[pos] = new TrieNode();
                }
                curNode = curNode.childNode[pos];
            }
            curNode.index = index;
            curNode.list.add(index);
        }

        public void search(String word, int index, List<List<Integer>> res) {
            TrieNode curNode = root;

            for (int i = 0; i < word.length(); i++) {
                // curNode.index >= 0 意味着这里有输入数组里的string
                // curNode.index != index 说明curNode.index对应的string和index对应的string不同
                // 当word[i...word.length() - 1]是回文串时，说明curNode.index对应的string和index对应的string可以连在一起形成回文串
                if (curNode.index >= 0 && curNode.index != index && isPalindrome(word, i, word.length() - 1)) {
                    res.add(Arrays.asList(index, curNode.index));
                }

                int pos = word.charAt(i) - 'a';

                if (curNode.childNode[pos] == null) {
                    return;
                }
                curNode = curNode.childNode[pos];
            }

            // 当word在字符串中找到与它相同的string后， 查看curNode.list里的index， 这些index对应的word都是在curNode后面的substring为回文串
            for (int j : curNode.list) {
                if (j != index) {
                    res.add(Arrays.asList(index, j));
                }
            }
        }
    }

    public List<List<Integer>> palindromePairs(String[] words) {
        List<List<Integer>> res = new ArrayList<>();

        if (words == null || words.length == 0) {
            return res;
        }

        Trie trie = new Trie();

        for (int i = 0; i < words.length; i++) {
            trie.insert(words[i], i);
        }

        for (int i = 0; i < words.length; i++) {
            trie.search(words[i], i, res);
        }
        return res;
    }

    public boolean isPalindrome(String word, int start, int end) {
        while (start < end) {
            if (word.charAt(start) != word.charAt(end)) {
                return false;
            }
            ++start;
            --end;
        }
        return true;
    }
}
```

## 3. Time & Space Complexity

**Trie**: 时间复杂度O(nL^2)，n是words里的元素个数，L是words里word的平均长度。空间复杂度O(nL)
