Description:
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]为回文。
问题分析
两种做法
- 假设当前字符串为words[i],对wordsi遍历,将其分成两部分,分别对两部分为回文的情况进行处理。例子,若words[i] = “aabc”,那么将其分成”aa”,”bc”,由于”aa”为回文,只需找到”bc”的逆字符串,将其插入”aabc”的前面就可以形成回文。
- 使用字典树。这种方法可以看一下这个链接,解释得非常详细:
https://leetcode.com/problems/palindrome-pairs/discuss/79195/O(n*k2)-java-solution-with-Trie-structure
解法1:
class Solution {
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> res = new ArrayList();
if(words == null || words.length < 2) return res;
//存放字符串对应下标
Map<String, Integer> map = new HashMap();
int len = words.length;
for(int i = 0;i < len;i++) map.put(words[i], i);
for(int i = 0;i < len;i++){
for(int j = 0;j <= words[i].length();j++){
//将words[i]分为两部分,分别对两部分为回文的情况进行处理
String str1 = words[i].substring(0, j);
String str2 = words[i].substring(j);
if(isPalindrome(str1)){
String str2reverse = new StringBuilder(str2).reverse().toString();
if(map.containsKey(str2reverse) && map.get(str2reverse) != i){
List<Integer> list = new ArrayList();
list.add(map.get(str2reverse));
list.add(i);
res.add(list);
}
}
if(isPalindrome(str2) && str2.length() != 0){
String str1reverse = new StringBuilder(str1).reverse().toString();
if(map.containsKey(str1reverse) && map.get(str1reverse) != i){
List<Integer> list = new ArrayList();
list.add(i);
list.add(map.get(str1reverse));
res.add(list);
}
}
}
}
return res;
}
//判断str是否为回文
public boolean isPalindrome(String str){
int i = 0, j = str.length() - 1;
while(i < j){
if(str.charAt(i) != str.charAt(j)) return false;
i++;
j--;
}
return true;
}
}
解法2(字典树)
class Solution {
private class TrieNode{
//存放字符串对应下标,若该节点在字典树中不是单词,则默认-1
int index;
//存放以当前字符串为后缀,剩余字符串为回文的字符串的下标
List<Integer> list;
TrieNode[] next;
public TrieNode(){
index = -1;
list = new ArrayList();
next = new TrieNode[26];
}
}
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> res = new ArrayList();
if(words == null || words.length == 0) return res;
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++) searchWord(root, i, words[i], res);
return res;
}
private void addWord(TrieNode root, String word, int index){
for(int i = word.length() - 1;i >= 0;i--){
int j = word.charAt(i) - 'a';
if(root.next[j] == null) root.next[j] = new TrieNode();
//若word的子串[i, i]为回文,说明以word的子串[i + 1, word.length()]为后缀
//且剩余字符串为回文
if(isPalindrome(word, 0, i)) root.list.add(index);
root = root.next[j];
}
root.index = index;
root.list.add(index);
}
private void searchWord(TrieNode root, int i, String word, List<List<Integer>> res){
for(int j = 0;j < word.length();j++){
//若word[0, j - 1]与字典树该节点对应的字符串匹配
//且word[j, word.length() - 1]为回文,说明(i, root.index)满足条件
if(root.index >= 0 && root.index != i && isPalindrome(word, j, word.length() - 1)){
res.add(Arrays.asList(i, root.index));
}
root = root.next[word.charAt(j)- 'a'];
if(root == null) return;
}
//注意这里
for(int j : root.list){
if(j == i) continue;
res.add(Arrays.asList(i, j));
}
}
private boolean isPalindrome(String str, int start, int end){
while(start < end){
if(str.charAt(start++) != str.charAt(end--)) return false;
}
return true;
}
}