(Java)-LeetCode-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"]


这道题,我首先想到的是暴力破解法,每两个字符串加起来,判断是否是回文串,果然超时了。代码如下:

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;
	}
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值