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"]
这道题,我首先想到的是暴力破解法,每两个字符串加起来,判断是否是回文串,果然超时了。代码如下:
public class Solution {
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
for(int i = 0; i < words.length; i++){
for(int j = 0; j < words.length; j++){
if(i != j && (words[i].length() == 0 || words[j].length() == 0 || words[i].charAt(0) == words[j].charAt(words[j].length()-1))){
if(isPalindrome(words[i]+words[j])){
List<Integer> list = new ArrayList<Integer>();
list.add(i);
list.add(j);
result.add(list);
}
}
}
}
return result;
}
private boolean isPalindrome(String s){
int left = 0;
int right = s.length()-1;
while(left <= right){
if(s.charAt(left) == s.charAt(right)){
left++;
right--;
}else
return false;
}
return true;
}
}
然后我看tags里有trie树,然后自己异想天开,是想建立两个trie树,第一个存放正常的字符串数组,第二个存放每个字符串都反转的字符串数组,然后对数组里的每个字符串,当其作为组成回文数的第一个数时,从前往后扫描每个字符,在逆序的trie树里查找,若找到字符串,则看看剩下的子字符串是否是回文的,若是,则成功,若不是则失败。
当其作为组成回文数的第二个数时,从后往前扫描每个字符,在正序的trie树里查找,若找到字符串,则看看剩下的子字符串是否是回文的,若是,则成功,若不是则失败。
这种算法只能找到比当前字符更短的匹配字符串,不过不会遗漏。然而这种异想天开的算法也超时了,为了纪念,代码如下所示:
public class Solution {
public List<List<Integer>> palindromePairs(String[] words) {
TrieST<Integer> trieordered = new TrieST<Integer>(Alphabet.BASE64);
TrieST<Integer> trieunorder = new TrieST<Integer>(Alphabet.BASE64);
Set<List<Integer>> set = new HashSet<List<Integer>>();
int i = 0;
for(String ss: words){
trieordered.put(ss, i);
trieunorder.put(new StringBuilder(ss).reverse().toString(), i++);
}
i=0;
for(String ss:words){
List<Integer> list1 = trieordered.conPalindrome(new StringBuilder(ss).reverse().toString());
for(int num : list1){
if(num != i){
List<Integer> templist = new ArrayList<Integer>();
templist.add(num);
templist.add(i);
set.add(templist);
}
}
List<Integer> list2 = trieunorder.conPalindrome(ss);
for(int num : list2){
if(num != i){
List<Integer> templist = new ArrayList<Integer>();
templist.add(i);
templist.add(num);
set.add(templist);
}
}
i++;
}
List<List<Integer>> result = new ArrayList<List<Integer>>(set);
return result;
}
}
class TrieST<Value> {
private Alphabet alpha;
private static int R;
private Node root;
public TrieST(Alphabet alpha){
this.alpha = alpha;
R = alpha.R();
}
private static class Node{
private Object val;
private Node[] next = new Node[R];
}
@SuppressWarnings("unchecked")
public Value get(String key){
Node x = get(root, key, 0);
if(x == null)
return null;
return (Value)x.val;
}
private Node get(Node x, String key, int d) {
// TODO Auto-generated method stub
if(x == null)
return null;
if(d == key.length())
return x;
char c = key.charAt(d);
return get(x.next[alpha.toIndex(c)],key,d+1);
}
public void put(String key, Value v){
root = put(root, key, v, 0);
}
private Node put(Node x, String key, Value val, int d){
if(x == null)
x = new Node();
if(d == key.length()){
x.val = val;
return x;
}
char c = key.charAt(d);
x.next[alpha.toIndex(c)] = put(x.next[alpha.toIndex(c)], key, val, d+1);
return x;
}
public List<Value> conPalindrome(String str){
List<Value> result = new ArrayList<Value>();
Node node = root;
int n = 0;
if(node.val != null){
if(isPalindrome(str.substring(n,str.length()))){
result.add((Value)root.val);
}
}
for(int i = 0 ; i < str.length(); i++){
n++;
char c = str.charAt(i);
node = node.next[alpha.toIndex(c)];
if(node == null){
break;
}
if(node.val != null){
if(isPalindrome(str.substring(n,str.length()))){
result.add((Value)node.val);
}
}
}
return result;
}
private boolean isPalindrome(String s){
int left = 0;
int right = s.length()-1;
while(left <= right){
if(s.charAt(left) == s.charAt(right)){
left++;
right--;
}else
return false;
}
return true;
}
}
class Alphabet {
public static final Alphabet BASE64 = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
private char[] alphabet; // the characters in the alphabet
private int[] inverse; // indices
private int R; // the radix of the alphabet
public Alphabet(String alpha) {
// check that alphabet contains no duplicate chars
boolean[] unicode = new boolean[Character.MAX_VALUE];
for (int i = 0; i < alpha.length(); i++) {
char c = alpha.charAt(i);
if (unicode[c])
throw new IllegalArgumentException("Illegal alphabet: repeated character = '" + c + "'");
unicode[c] = true;
}
alphabet = alpha.toCharArray();
R = alpha.length();
inverse = new int[Character.MAX_VALUE];
for (int i = 0; i < inverse.length; i++)
inverse[i] = -1;
// can't use char since R can be as big as 65,536
for (int c = 0; c < R; c++)
inverse[alphabet[c]] = c;
}
public int R() {
return R;
}
public int toIndex(char c) {
if (c >= inverse.length || inverse[c] == -1) {
throw new IllegalArgumentException("Character " + c + " not in alphabet");
}
return inverse[c];
}
public char toChar(int index) {
if (index < 0 || index >= R) {
throw new IndexOutOfBoundsException("Alphabet index out of bounds");
}
return alphabet[index];
}
}
然后在网上看看其他人的想法,如下所示:
利用字典wmap保存单词 -> 下标的键值对 遍历单词列表words,记当前单词为word,下标为idx: 1). 若当前单词word本身为回文,且words中存在空串,则将空串下标bidx与idx加入答案 2). 若当前单词的逆序串在words中,则将逆序串下标ridx与idx加入答案 3). 将当前单词word拆分为左右两半left,right。 3.1) 若left为回文,并且right的逆序串在words中,则将right的逆序串下标rridx与idx加入答案 3.2) 若right为回文,并且left的逆序串在words中,则将left的逆序串下标idx与rlidx加入答案O(k * n ^2)解法其中k为单词个数,n为单词的长度
其实1和2只是3的特殊情况
我最开始实现还是用trie树,不过这次内存不够用了(在字符串很长的情况下),本来trie树就是用空间换时间的嘛,于是换用了HashMap,就够了~代码如下:
public class Solution {
public List<List<Integer>> palindromePairs(String[] words) {
Map<String,Integer> trieordered = new HashMap<String,Integer>();
Set<List<Integer>> set = new HashSet<List<Integer>>();
int i = 0;
for(String ss: words){
trieordered.put(ss, i++);
}
i = 0;
for(String ss:words){
int len = ss.length();
for(int index = 0 ; index <= len; index++ ){
String left = ss.substring(0, index);
String right = ss.substring(index,len);
if(isPalindrome(left)){
Integer temp = trieordered.get(new StringBuilder(right).reverse().toString());
if(temp != null && temp != i){
List<Integer> list = new ArrayList<Integer>();
list.add(temp);
list.add(i);
set.add(list);
}
}
if(isPalindrome(right)){
Integer temp = trieordered.get(new StringBuilder(left).reverse().toString());
if(temp != null && temp != i){
List<Integer> list = new ArrayList<Integer>();
list.add(i);
list.add(temp);
set.add(list);
}
}
}
i++;
}
List<List<Integer>> result = new ArrayList<List<Integer>>(set);
return result;
}
private boolean isPalindrome(String s){
int left = 0;
int right = s.length()-1;
while(left <= right){
if(s.charAt(left) == s.charAt(right)){
left++;
right--;
}else
return false;
}
return true;
}
}