算法练习-回文对

题目描述

题目描述:
给定一组 互不相同 的单词, 找出所有不同 的索引对(i, j),使得列表中的两个单词, words[i] + words[j] ,可拼接成回文串。

 

示例 1:

输入:["abcd","dcba","lls","s","sssll"]
输出:[[0,1],[1,0],[3,2],[2,4]] 
解释:可拼接成的回文串为 ["dcbaabcd","abcddcba","slls","llssssll"]
示例 2:

输入:["bat","tab","cat"]
输出:[[0,1],[1,0]] 
解释:可拼接成的回文串为 ["battab","tabbat"]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

暴力破解-枚举前缀和后缀
首先,我们假设有两个串s1和s2,s1+s2合并后是回文串,那么s1和s2会符合这样的情况:

  1. s1.length() == s2.length(): s1一定是s2的反转。如s1=“abc” ,s2 = “cba”,s1+s2=“abccba”回文,假如不反转就不会回文。
  2. s1.length() > s2.length(): 那么s1拆成左右两部分t1和t2,t1是s2的反转,而t2是回文串。如s1=“abcc”,s2=“ba”,s1+s2=“abccba”回文,s1拆分为t1=“ab”,t2=“cc”,那么t1=“ab”是s2=“ba”的反转,而t2=“cc”是回文串。
  3. s1.length() < s2.length(): 这种情况就相当于上面情况2的倒转,这里不做重复描述了。

因此这样,对于每一个字符串,我们令其为 s1 和 s2中较长的那一个,然后找到可能和它构成回文串的字符串即可。

换句话说,就是枚举s1和s2,令其中最长的那个,拆分成两个部分,t1和t2:

  1. 假如t1是回文串,符合情况3,我们只需要查询给定的字符串序列中是否包含 t2 的翻转
  2. 假如t2是回文串。符合情况2,我们只需要查询给定的字符串序列中是否包含 t1 的翻转

也就是说,我们要枚举字符串 k 的每一个前缀和后缀,判断其是否为回文串。如果是回文串,我们就查询其剩余部分的翻转是否在给定的字符串序列中出现即可。

因此,我们可以使用哈希表存储所有字符串的翻转串。在进行查询时,我们判断带查询串的子串是否在哈希表中出现,就等价于判断了其翻转是否存在。

代码

//code
class Solution {
	//用于记录反转后的字符串
    List<String> wordsRev = new ArrayList<String>();
    //使用hash表,用于记录反转字符串以及对应的下标
    Map<String, Integer> indices = new HashMap<String, Integer>();

    public List<List<Integer>> palindromePairs(String[] words) {
        int n = words.length;
        //字符串反转,加入链表
        for (String word: words) {
            wordsRev.add(new StringBuffer(word).reverse().toString());
        }
        //把链表中的反转字符串用hash表记录
        for (int i = 0; i < n; ++i) {
            indices.put(wordsRev.get(i), i);
        }
		//定义结果集
        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        
        for (int i = 0; i < n; i++) {
            String word = words[i];
            int m = words[i].length();
            if (m == 0) { //空串也是回文,所以跳过
                continue;
            }
            for (int j = 0; j <= m; j++) {
                if (isPalindrome(word, j, m - 1)) {
                    int leftId = findWord(word, 0, j - 1);
                    if (leftId != -1 && leftId != i) {
                        ret.add(Arrays.asList(i, leftId));
                    }
                }
                if (j != 0 && isPalindrome(word, 0, j - 1)) {
                    int rightId = findWord(word, j, m - 1);
                    if (rightId != -1 && rightId != i) {
                        ret.add(Arrays.asList(rightId, i));
                    }
                }
            }
        }
        return ret;
    }
	
	//判断字符串下标为left到right部分是否回文
    public boolean isPalindrome(String s, int left, int right) {
        int len = right - left + 1;
        for (int i = 0; i < len / 2; i++) {
            if (s.charAt(left + i) != s.charAt(right - i)) {
                return false;
            }
        }
        return true;
    }

	//获取hash表中反转单词对应的下标,假如不存在该单词就返回-1
    public int findWord(String s, int left, int right) {
        return indices.getOrDefault(s.substring(left, right + 1), -1);
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/palindrome-pairs/solution/hui-wen-dui-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值