Leetcode 336 回文对

示例 1:

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

示例 2:

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

示例 3:

输入:words = ["a",""]
输出:[[0,1],[1,0]]

提示:

  • 1 <= words.length <= 5000
  • 0 <= words[i].length <= 300
  • words[i] 由小写英文字母组成

解法1:暴力数组法

我们知道要单词i和单词j能组成回文,那么单词i的第一个字符肯定和单词j的最后一个字符相等。

char(0) of word[i] == char(length-1) of word(j)

那么,我们可以把最后一个字符相同的单词放到同一个字符为下标的数组里。在遍历比较的时候,通过单词i取它的首字母为下标的列表里的值list(word[j])进行逐个比较,判断若是回文则直接输出。

代码如下:

class Solution {public List<List<Integer>> palindromePairs(String[] words) {int len = words.length;Map<String, Integer> indexMap = new HashMap<>(len * 4/3);// char list, contains ends with the characterList[] postfixList = new List[128];for (int i=0; i<len; i++) {// save word indexindexMap.put(words[i], i);int wordLen = words[i].length();// skip empty "" if (wordLen > 0) {char lastChar = words[i].charAt(wordLen-1);if (postfixList[lastChar] == null) {postfixList[lastChar] = new ArrayList<>();}postfixList[lastChar].add(words[i]);}}List<List<Integer>> result = new ArrayList<>();boolean containsEmpty = indexMap.containsKey("");for (int i=0; i<len; i++) {String word = words[i];int wordLen = word.length();// skip empty stringif (wordLen == 0) {continue;}// process postfix listchar firstChar = word.charAt(0);List<String> posts = postfixList[firstChar];if (posts != null) {for(int j=0; j<posts.size(); j++) {String candidate = posts.get(j);if(!word.equals(candidate) && isPalindrome(word + candidate)) {System.out.println("postfix word: "+ words[i]+", candidate:"+candidate);result.add(Arrays.asList(i, indexMap.get(candidate)));}}}// process empty string if existedif (containsEmpty && isPalindrome(word)) {int emptyIndex = indexMap.get("");result.add(Arrays.asList(i, emptyIndex));result.add(Arrays.asList(emptyIndex, i));}}return result;}public boolean isPalindrome(String str) {char[] chars = str.toCharArray();for (int i=0, j=chars.length-1; i<j; i++,j--) {if (chars[i] != chars[j]){return false;}}return true;}
}

运行结果:跑到第134个测试用例超时。

分析原因发现:测试用例134大部分是以相同字母结尾的,那么就退变成每个单词都要比较所有单词,所以效率低。

解法2:前缀树(字典树)

1)使用前缀树来存储逆序后的单词。

2)把单词word[i]按字母拆分 prefix:string[0,i], postfix [i+1,len-1],有下面两种情况:

i)  假如后半部分为回文,则用前半分去查询前缀树得到对于的翻转串 s' (对应的单词j)。 prefix + palindrome(postfix) + s' 那么组成的新词一定为回文,得到的结果为(i,j);

ii) 假如前半部分为回文,在用后半部分去查询前缀树得到的翻转串s' (对应的单词j)。s'+palindrome(prefix)+postifix, 得到的结果为(j, i) 

字典树的参考实现:

java - 一文搞懂字典树 - bigsai - SegmentFault 思否

Java数据结构---Trie(字典树/前缀树)_java 数据结构 trible-CSDN博客

代码如下:

class Solution {public List<List<Integer>> palindromePairs(String[] words) {// build trie tree with reversed wordint len = words.length;TrieTree tree = new TrieTree();for(int i=0; i<len; i++) {StringBuffer reversed = new StringBuffer(words[i]).reverse();// full reversetree.insert(reversed, i);}List<List<Integer>> result = new ArrayList<>();for (int i = 0; i < len; i++) {String word = words[i];for (int j=1; j<=word.length(); j++) {String prefix = word.substring(0, j);String postfix = word.substring(j);if (isPalindrome(prefix)) {// search word in the reversed trie treeint wordIndex = tree.search(postfix);if (wordIndex >= 0 && wordIndex != i) {result.add(List.of(wordIndex, i));}}if (isPalindrome(postfix)) {// search word in the reversed trie treeint wordIndex = tree.search(prefix);if (wordIndex >= 0 && wordIndex != i) {result.add(List.of(i, wordIndex));}}}// process empty stringif (word.length() == 0) {for (int j=0; j<len; j++) {if (i != j && isPalindrome(words[j])) {result.add(List.of(j, i));}}}}return result;}/*** double pointer checking palindrome.* * @param wordChars* @param candidate* @return*/public boolean isPalindrome(String str) {char[] chars = str.toCharArray();for (int i=0, j=chars.length-1; i<j; i++,j--) {if (chars[i] != chars[j]){return false;}}return true;}}class TrieTree {private TrieNode root;public TrieTree() {root = new TrieNode();}/*** build trie tree and save index into end node.* @param word* @param index*/public void insert(StringBuffer word, int index) {TrieNode current = root;int len = word.length();for (int i=0; i<len; i++) {char ch = word.charAt(i);current.children.putIfAbsent(ch, new TrieNode());current = current.children.get(ch);}current.wordIndex = index;}/*** search the word in trie tree.* @param word* @return*/public int search(String word) {TrieNode current = root;for (char ch: word.toCharArray()) {TrieNode node = current.children.get(ch);if (node == null) {return -2;}current = node;}return current.wordIndex;}public boolean isPalindrome(StringBuilder str) {int len = str.length();for (int i=0, j=len-1; i<j; i++,j--) {if (str.charAt(i) != str.charAt(j)){return false;}}return true;}public class TrieNode {// if this is a word, wordIndex >= 0, or else = -1int wordIndex = -1;Map<Character, TrieNode> children = new HashMap<>();}
}

运行结果如下:

前进了一小步,测试用例从134进步到了135,还是未通过。

分析原因:发现这里给的词都是对称的长度300,在150,151开始变化,前缀树深度很深在150~151才开始分化。

优化:既然前缀树退化成了链表,需要全部遍历才能获取结果。那么使用HashMap是不是应该很快呢?

解法3:HashMap存储逆序字符串

算法还是算法2的步骤,只不过把存储逆序的结构换成了HashMap,这个才更好理解和快速嘛。

代码如下:
 

class Solution {public List<List<Integer>> palindromePairs(String[] words) {// build trie tree with reversed wordint len = words.length;Map<String,Integer> reversedMap = new HashMap<>(len*4/3);for(int i=0; i<len; i++) {StringBuffer reversed = new StringBuffer(words[i]).reverse();reversedMap.put(reversed.toString(), i);}List<List<Integer>> result = new ArrayList<>();for (int i = 0; i < len; i++) {String word = words[i];char[] wordChars = word.toCharArray();for (int j=1; j<=wordChars.length; j++) {// prefix is palindrome, search postfixif (isPalindrome(wordChars, 0, j)) {// search word in the reversed mapInteger wordIndex = reversedMap.get(word.substring(j));if (wordIndex != null && wordIndex >= 0 && wordIndex != i) {result.add(List.of(wordIndex, i));}}// postfix is palindrome, search prefixif (isPalindrome(wordChars, j, wordChars.length)) {// search word in the reversed mapInteger wordIndex = reversedMap.get(word.substring(0, j));if (wordIndex != null && wordIndex >= 0 && wordIndex != i) {result.add(List.of(i, wordIndex));}}}// process empty stringif (word.length() == 0) {for (int j=0; j<len; j++) {char[] chars = words[j].toCharArray();if (i != j && isPalindrome(chars, 0, chars.length)) {result.add(List.of(j, i));}}}}return result;}/*** double pointer checking palindrome, not included end; [start, end)* * @param wordChars* @param candidate* @return*/public boolean isPalindrome(char[] wordChars, int start, int end) {for (int i=start, j=end-1; i<j; i++,j--) {if (wordChars[i] != wordChars[j]){return false;}}return true;}}

执行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Vue+SpringBoot的考研学习分享平台设计与实现

摘要 考研学习分享平台是一个专注于为考研学子提供全面学习资源和经验分享的互动社区。这里汇聚了众多考研成功者的经验心得&#xff0c;涵盖各个学科领域的备考技巧和策略。平台不仅提供丰富的学习资料&#xff0c;还设有在线答疑、模拟考试等实用功能&#xff0c;帮助考生高…

【C++】C++11新特性详解:可变参数模板与emplace系列的应用

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriori…

TCP为什么需要三次握手?两次握手或四次握手可以吗?

&#xff08;1&#xff09;三次握手可以保证双方具有接收和发送的能力 第一次握手服务端可以确认客户端的发送能力和服务端的接收能力是正常的&#xff1b;第二次握手客户端可以确认客户端和服务端的收发能力是正常的&#xff0c;但是服务端无法确认客户端的接收能力是正常的&…

nature communications论文 解读

题目《Transfer learning with graph neural networks for improved molecular property prediction in the multi-fidelity setting》 这篇文章主要讨论了如何在多保真数据环境&#xff08;multi-fidelity setting&#xff09;下&#xff0c;利用图神经网络&#xff08;GNNs&…

Cmakelist.txt之Linux-redis配置

1.cmakelist.txt cmake_minimum_required(VERSION 3.16) ​ project(redis_linux_test LANGUAGES C) ​ ​ ​ add_executable(redis_linux_test main.c) ​ # 设置hiredis库的头文件路径和库文件路径 set(Hiredis_INCLUDE_DIR /usr/local/include/hiredis) set(Hiredis_LIBRA…

unity小:Unity WebGL 嵌入前端网页中的iframe并实现双向通信

可以使用 Unity 的 SendMessage 方法和 JavaScript 的 postMessage 方法来实现。以下是一个基本的步骤指南&#xff1a; 1. 在 Unity 中设置 WebGL 项目 首先&#xff0c;你需要在 Unity 中构建一个 WebGL 项目&#xff1a; 打开 Unity 项目。选择 File -> Build Setting…

c ++零基础可视化——vector

c 零基础可视化——vector 初始化 vector<int> v0(5); // 0 0 0 0 0 vector<int> v1(5, 1); // 1 1 1 1 1 vector<int> v2{1, 2, 3} // 1 2 3 vector<int> v3(v1); // 1 1 1 1 1 vector<vector<int>> v4(2, vect…

基于Qt/C++/Opencv实现的一个视频中二维码解析软件

本文详细讲解了如何利用 Qt 和 OpenCV 实现一个可从视频和图片中检测二维码的软件。代码实现了视频解码、多线程处理和界面更新等功能&#xff0c;是一个典型的跨线程图像处理项目。以下分模块对代码进行解析。 一、项目的整体结构 项目分为以下几部分&#xff1a; 主窗口 (M…

常见的 git 提交备注类型

在 Git 提交中&#xff0c;常见的提交备注&#xff08;commit message&#xff09;遵循一定的约定&#xff0c;这有助于代码管理、版本控制以及团队协作。fix 和 feat 是常见的提交类型&#xff0c;下面是这两个的含义&#xff0c;以及其他常见的提交类型。 常见的 Git 提交备…

android-studio-4.2下载 、启动

下载 分享一个国内的android studio网站&#xff0c;可以下载SDK和一些Android studio开发工具 https://www.androiddevtools.cn/ 启动 JAVA_HOME/app/zulu17.48.15-ca-jdk17.0.10-linux_x64/ /app5/android-studio-home/android-studio-ide-201.6568795-linux-4.2C1/bin/s…

C语言练习.if.else语句.strstr

今天在做题之前&#xff0c;先介绍一下&#xff0c;新学到的库函数strstr 想要使用它&#xff0c;要先给它一个头文件<string.h> char *strstr(const char*str1,const char*str2); 首先&#xff1a;1.strstr的返回值是char&#xff0c;字符类型的。 2.两个实参&#xff…

丹摩|丹摩智算平台深度评测

1. 丹摩智算平台介绍 随着人工智能和大数据技术的快速发展&#xff0c;越来越多的智能计算平台涌现&#xff0c;为科研工作者和开发者提供高性能计算资源。丹摩智算平台作为其中的一员&#xff0c;定位于智能计算服务的提供者&#xff0c;支持从数据处理到模型训练的全流程操作…

美创科技入选2024数字政府解决方案提供商TOP100!

11月19日&#xff0c;国内专业咨询机构DBC德本咨询发布“2024数字政府解决方案提供商TOP100”榜单。美创科技凭借在政府数据安全领域多年的项目经验、技术优势与创新能力&#xff0c;入选收录。 作为专业数据安全产品与服务提供商&#xff0c;美创科技一直致力于为政府、金融、…

CSS —— 子绝父相

相对定位&#xff1a;占位&#xff1b;不脱标 绝对定位&#xff1a;不占位&#xff1b;脱标 希望子元素相对于父元素定位&#xff0c;又不希望父元素脱标&#xff08;父元素占位&#xff09; 子级是 绝对定位&#xff0c;不会占有位置&#xff0c; 可以放到父盒子里面的任何一…

废品买卖回收管理系统|Java|SSM|Vue| 前后端分离

【重要①】前后端源码万字文档部署文档 【重要②】正版源码有问题包售后 【包含内容】 【一】项目提供非常完整的源码注释 【二】相关技术栈文档 【三】源码讲解视频 【其它服务】 【一】可以提供远程部署安装&#xff0c;包扩环境 【…

webpack配置和打包性能优化

文章目录 webpack基础配置loaderpluginloader 和 plugin 的区别devServer打包性能优化1、按需引入组件2、externals 属性3、给定文件匹配范围4、noParse 属性5、cacheDirectory 缓存属性6、happyPack 多个子进程并行 webpack基础配置 mode:development&#xff1a;设置webpack…

Flink学习连载第二篇-使用flink编写WordCount(多种情况演示)

使用Flink编写代码&#xff0c;步骤非常固定&#xff0c;大概分为以下几步&#xff0c;只要牢牢抓住步骤&#xff0c;基本轻松拿下&#xff1a; 1. env-准备环境 2. source-加载数据 3. transformation-数据处理转换 4. sink-数据输出 5. execute-执行 DataStream API开发 //n…

构建高效在线教育:SpringBoot课程管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理在线课程管理系统的相关信息成为必然。开发…

【Redis_Day6】Hash类型

【Redis_Day6】Hash类型 Hash类型操作hash的命令hset&#xff1a;设置hash中指定的字段&#xff08;field&#xff09;的值&#xff08;value&#xff09;hsetnx&#xff1a;想hash中添加字段并设置值hget&#xff1a;获取hash中指定字段的值hexists&#xff1a;判断hash中是否…

elasticsearch的文档管理

2 json数据入门 json数据类型 基础数据类型 字符串&#xff0c;只要用双引号包裹的都是字符串类型。“嘻嘻”&#xff0c;“文勇”&#xff0c;“2024” 数字型&#xff0c;2024&#xff0c;3.1415926 布尔型&#xff0c;true 和 false 空值&#xff0c;null 高级数据类…