本文已参与「新人创作礼」活动, 一起开启掘金创作之路
- 双周赛第69场:LeetCode:5962.连接单词得到的最长回文串
- 时间:2022-01-09
- 力扣难度:Medium
- 个人难度:Medium
- 数据结构:哈希表、字符串
- 算法:哈希计数
双周赛第69场:LeetCode:5962.连接单词得到的最长回文串
1. 题目描述
-
题目:原题链接
- 给你一个字符串数组 words,words 中每个元素都是一个包含两个小写英文字母的单词。
- 请你从 words 中选择一些元素并按任意顺序连接它们,并得到一个尽可能长的回文串,回文串指的是从前往后和从后往前读一样的字符串。
- 每个元素至多只能使用一次,请你返回你能得到的最长回文串的长度。
- 如果没办法得到任何一个回文串,请你返回 0 。
- 注意:每一个word都是两个小写字母
-
输入输出规范
- 输入:字符串数组words
- 输出:最长的回文串的长度
-
输入输出示例
- 输入:words = ["ab","ty","yt","lc","cl","ab"]
- 输出:8
2. 方法:哈希计数+奇偶分析
-
思路:哈希计数+奇偶分析
-
根据题意可知,字符串数组中,存在两种类型的单词,同时,单词可能会重复,所以需要进行计数,注意单词均为两个小写字母,这可以简化计数方式
- 本身就是回文串的单词,如:aa,对于这种类型,可以使用
new int[26]数组来进行哈希计数 - 本身不是回文串的单词,如:ab,对于这种类型,可以使用Map来计数
- 此外,还可以通过二维数组
int[26][26]来计数,本题解没有使用这种方式,感兴趣的可以尝试下
- 本身就是回文串的单词,如:aa,对于这种类型,可以使用
-
除此之外,本题需要根据单词是否回文串分类讨论
-
情况一:回文串单词,此时需要先进行哈希计数(第一次遍历),之后再根据每个存在的单词的个数的奇偶性进行讨论(第二次遍历)
- 个数为偶数时:可以对称的放到最终的最长回文串的两侧,即此时总长度满足
len += count * 2 - 个数为奇数时:可以对称的将小于该奇数的最大偶数对称放在两侧,同样有
len += (count - 1) * 2,并且额外多出来的一个回文单词,可以放在最长回文串的正中间,即len += 2
- 个数为偶数时:可以对称的放到最终的最长回文串的两侧,即此时总长度满足
-
情况二:非回文串单词,此时可以在进行哈希计数的同时,判断该单词的逆序单词是否存在于哈希表中(一次遍历)
- 存在时可以将其分别放于最长回文串的两侧,有
len += 4 - 不存在时将当前单词放入哈希表,等待验证后续单词是否会与其匹配
- 存在时可以将其分别放于最长回文串的两侧,有
![
- 判断单词是否回文的方式可以借助
StringBuilder的reverse()方法
-
-
复杂度分析:n 是字符串数组的长度,即单词个数
- 时间复杂度:O(n)
- 空间复杂度:O(n)
-
题解
public int longestPalindrome(String[] words) { if (words == null || words.length == 0) return 0; Map<String, Integer> wordMap = new HashMap<>(); // 存放不是回文串的word int[] hash = new int[26]; int palindromeLen = 0; String reverse = ""; for (String word : words) { StringBuilder sb = new StringBuilder(); reverse = sb.append(word).reverse().toString(); // 分类讨论,本身就是回文串的word,直接计入最长回文串 if (word.equals(reverse)) { // if (word.charAt(0) == word.charAt(1)) hash[word.charAt(0) - 'a']++; } else { // 本身不是回文串的word,去匹配对应的reverse结构 if (wordMap.get(reverse) != null && wordMap.get(reverse) > 0) { // 匹配成功,可以添加到最长回文串的两端,len + 2 * len(word) palindromeLen += 4; wordMap.put(reverse, wordMap.get(reverse) - 1); } else { // 匹配失败,将当前word加入wordMap wordMap.put(word, wordMap.getOrDefault(word, 0) + 1); } } } boolean last = false; for (int count : hash) { if ((count & 1) == 0) { palindromeLen += 2 * count; // word.length() } else { palindromeLen += 2 * (count - 1); // word.length() last = true; } } if(last) palindromeLen += 2; return palindromeLen; }
3. 方法二:优化
-
思考与优化
-
思考:本题的输入具有一定的限制
- 限制一:单词都是两个字母
- 限制二:单词都是小写字母
-
优化
- 对于限制一,此时对放在正中间的回文单词没有要求,如果单词的字母个数没有限制的话,如:a、cc、sss,此时放在正中间的应该是最长的回文单词
- 对于限制二,此时可以使用数组哈希,如果单词可以有大小写字母甚至一些其他符号,此时应该使用键值对哈希
-
-
复杂度分析:n 是字符串数组的长度,即单词个数
- 时间复杂度:O(n)
- 空间复杂度:O(n)
-
题解
public int longestPalindrome(String[] words) { if (words == null || words.length == 0) return 0; Map<String, Integer> wordMap = new HashMap<>(); // 存放不是回文串的word Map<String, Integer> palindromeMap = new HashMap<>(); // 存放是回文串的word int palindromeLen = 0; String reverse = ""; for (String word : words) { StringBuilder sb = new StringBuilder(); reverse = sb.append(word).reverse().toString(); // 分类讨论,本身就是回文串的word,直接添加到map中,后面在进行分析 if (word.equals(reverse)) { // if (word.charAt(0) == word.charAt(1)) palindromeMap.put(word, palindromeMap.getOrDefault(word, 0) + 1); } else { // 本身不是回文串的word,去匹配对应的reverse结构 if (wordMap.get(reverse) != null && wordMap.get(reverse) > 0) { // 匹配成功,可以添加到最长回文串的两端,len + 2 * len(word) palindromeLen += word.length() << 1; wordMap.put(reverse, wordMap.get(reverse) - 1); } else { // 匹配失败,将当前word加入wordMap wordMap.put(word, wordMap.getOrDefault(word, 0) + 1); } } } // 分析回文word的情况,对应palindromeMap int maxSingleLen = 0; Set<Map.Entry<String, Integer>> wordEntries = palindromeMap.entrySet(); for (Map.Entry<String, Integer> wordEntry : wordEntries) { // 偶数个回文word,直接全部计入最长回文串长度中 if ((wordEntry.getValue() & 1) == 0) { palindromeLen += wordEntry.getKey().length() * wordEntry.getValue(); }else { // 奇数个回文word,只能直接将(count - 1)个word计入最长回文串长度中,最后一个与其他单独的word进行长度比较,选择最长的 palindromeLen += wordEntry.getKey().length() * (wordEntry.getValue() - 1); maxSingleLen = Math.max(maxSingleLen, wordEntry.getKey().length()); } } // 将选择的最长的单个回文word计入最长回文串长度中 palindromeLen += maxSingleLen; return palindromeLen; }
最后
新人LeetCoder,发布的题解有些会参考其他大佬的思路(参考资料的链接会放在最下面),欢迎大家关注我 ~ ~ ~ 如果本文有所帮助的话,希望大家可以给个三连「点赞」&「收藏」&「关注」 ~ ~ ~ 也希望大家有空的时候光临我的「个人博客」。