Hard-题目47:336. Palindrome Pairs

题目原文:
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”]
题目大意:
给出一个单词列表,其中每个单词都是不同的。寻找所有的不同的索引对(I,j)使得words[i]+words[j]是回文串。
题目分析:
对于n个元素的单词列表,单词长度不超过k,很容易想到暴力解法,复杂度为O(kn^2),提交显然tle。
看到discuss中一位大神的解法,大致理解如下:
把每个字符串翻转后构建Trie树,trie树的每个节点增加两个信息:
int index,如果该点是单词,则记录在原列表的下标,如果不是,该值为-1
List list, 这个列表有点难理解,先摘抄原文如下:
Here I take another strategy: add an integer list to each TrieNode; the list will record the indices of all words satisfying the following two conditions: each word has a suffix represented by the current Trie node; the rest of the word forms a palindrome.
我是这样理解这段话的:list记录的是索引值,其中索引值对应的单词均以这个Trie节点为后缀(因为这棵trie树是反向构造的,所以这点代表的前缀对应为单词的后缀),并且该字符串去掉这一点对应串的后缀是个回文串。
构建完这样的trie树后,遍历单词列表,带上下标i放到trie树中搜索,因为trie树是反着存的,所以如果存在这样的回文串,必定能正向查找到一个节点j(例如列表里有abc和cba两个词,则trie树里存了abc这个节点),再看trie树中的abc节点的list,其中记录了所有以cba为结尾,剩下部分是回文串的下标,很显然这些字符串前面加上abc必是回文串!!!!!!!!
这里有两个trick,第一个是trie树的root节点是空,但列表中可能有空串,这样的情况下只需要看words[i]是不是回文即可。第二个trick是i本身若是回文串,则(i,i)也是回文的,但观察标程的输出发现i不能等于j(这个题目好像没有说清,不知道是不是我没理解好题意)。
源码:(language:java)

public class Solution {
    class TrieNode {
        TrieNode[] next;
        int index;
        List<Integer> list;
        TrieNode() {
            next = new TrieNode[26];
            index = -1;
            list = new ArrayList<>();
        }
    }
    public List<List<Integer>> palindromePairs(String[] words) {
        List<List<Integer>> res = new ArrayList<>();
        TrieNode root = new TrieNode();
        for (int i = 0; i < words.length; i++) 
            addWord(root, words[i], i);
        for (int i = 0; i < words.length; i++) 
            search(words, i, root, res);
        return res;
    }
    private void addWord(TrieNode root, String word, int index) {
        for (int i = word.length() - 1; i >= 0; i--) {
            if (root.next[word.charAt(i) - 'a'] == null) 
                root.next[word.charAt(i) - 'a'] = new TrieNode();
            if (isPalindrome(word, 0, i)) 
                root.list.add(index);
            root = root.next[word.charAt(i) - 'a'];
        }
        root.list.add(index);
        root.index = index;
    }
    private void search(String[] words, int i, TrieNode root, List<List<Integer>> list) {
        for (int j = 0; j < words[i].length(); j++) {   
            if (root.index >= 0 && root.index != i && isPalindrome(words[i], j, words[i].length() - 1)) 
                list.add(Arrays.asList(i, root.index));
            root = root.next[words[i].charAt(j) - 'a'];
            if (root == null) return;
        }

        for (int j : root.list) {
            if (i != j) 
                list.add(Arrays.asList(i, j));
        }
    }
    private boolean isPalindrome(String word, int i, int j) {
        while (i < j) {
            if (word.charAt(i++) != word.charAt(j--)) 
                return false;
        }
        return true;
    }
}

成绩:
63ms
Cmershen的碎碎念:
好题,很考验数据结构和算法设计能力。trie树是acm中的一种比较基础的数据结构,这题借助反向构建trie树来寻找回文串,应该在求职机试中会是难题,而且把如果数据出严一点,个人感觉放在acm竞赛里面也绝对不是一道水题。
Leetcode是面向面试的,如果哪位大仙能在面试的短时间和高心理压力下想得到这个算法,真的是大神了……

你可能感兴趣的:(Hard-题目47:336. Palindrome Pairs)