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…

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

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

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;包扩环境 【…

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中是否…

在SQLyog中导入和导出数据库

导入 假如我要导入一个xxx.sql&#xff0c;我就先创建一个叫做xxx的数据库。 然后右键点击导入、执行SQL脚本 选择要导入的数据库文件的位置&#xff0c;点击执行即可 注意&#xff1a; 导入之后记得刷新一下导出 选择你要导出的数据库 右键选择&#xff1a;备份/导出、…

详细教程-Linux上安装单机版的Hadoop

1、上传Hadoop安装包至linux并解压 tar -zxvf hadoop-2.6.0-cdh5.15.2.tar.gz 安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1u59OLTJctKmm9YVWr_F-Cg 提取码&#xff1a;0pfj 2、配置免密码登录 生成秘钥&#xff1a; ssh-keygen -t rsa -P 将秘钥写入认…

实时数据开发 | 怎么通俗理解Flink容错机制,提到的checkpoint、barrier、Savepoint、sink都是什么

今天学Flink的关键技术–容错机制&#xff0c;用一些通俗的比喻来讲这个复杂的过程。参考自《离线和实时大数据开发实战》 需要先回顾昨天发的Flink关键概念 检查点&#xff08;checkpoint&#xff09; Flink容错机制的核心是分布式数据流和状态的快照&#xff0c;从而当分布…

鸿蒙网络编程系列50-仓颉版TCP回声服务器示例

1. TCP服务端简介 TCP服务端是基于TCP协议构建的一种网络服务模式&#xff0c;它为HTTP&#xff08;超文本传输协议&#xff09;、SMTP&#xff08;简单邮件传输协议&#xff09;等高层协议的应用程序提供了可靠的底层支持。在TCP服务端中&#xff0c;服务器启动后会监听一个或…

DataGrip 连接 Redis、TongRDS

连接 Redis 或 TongRDS 有些旧版本 没有 redis 驱动用不了 1&#xff09;选择驱动 2&#xff09;添加连接信息 3&#xff09;测试连接 4&#xff09;保存连接 5&#xff09;使用案例

DevExpress控件 基本使用

DevExpress控件 一、DevExpress简介 1、所有编辑器的公共功能 全部都可以绑定数据&#xff1b; 全部都可以独立使用或用于由 Developer Express 提供的容器控件 (XtraGrid、XtraVerticalGrid、XtraTreeList 和 XtraBars) 内的内置编辑&#xff1b; 全部都使用相同的样式、外…