阿亮的算法之路——5. 最长回文子串

2024-01-07 02:59

本文主要是介绍阿亮的算法之路——5. 最长回文子串,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目详情

题目详情
题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/

初次完成

老规矩,确保自己的第一思路能先完成功能。反正是各种运行调试,有问题就debug

    public String longestPalindrome(String s){String maxSubstr = "";int in;a:for (int i = 0; i < s.length(); i++){String s1 = s.substring(i);char ch = s.charAt(i);b:while ((in = s1.lastIndexOf(ch)) != -1){s1 = s1.substring(0,in+1);for (int n = 0; n < s1.length()/2; n++){if (s1.charAt(n) != s1.charAt(s1.length()-n-1)){s1 = s1.substring(0,s1.length()-1);continue b;}}maxSubstr = maxSubstr.length()>s1.length()?maxSubstr:s1;continue a;}}return maxSubstr;}

感觉能想到的都是最笨的办法,完全没有什么利用算法的思想,幸好没给我说什么超时,不过这效率是真的低
第一轮提交思路就是:先定义一个最长的回文串为空字符,从头遍历这个字符串,每个字符,都从后面去找和它相同的字符,如果找到了相同的字符,再往中间判断是否为回文串,是就比较更新最长回文串,不是就再找下一个相同的字符……处理好各种边界条件就OK。

首次优化

关于优化,我首先想到的就是,我第一次提交成功的代码里面,每层循环里使用了很多的字符串截取操作,每遍历一个字符,下一次遍历我都将它截取为一个新的字符串。我觉得字符串的截取,应该挺浪费时间和空间的,所以我决定用StringBuilder来替换有字符串截取的地方。
将能替换的地方都替换为StringBuilder之后(有的地方无法替换),第二轮代码出来了:

    public String longestPalindrome(String s){StringBuilder st = new StringBuilder("");int in;a:for (int i = 0; i < s.length(); i++){String s1 = s.substring(i);char ch = s.charAt(i);b:while ((in = s1.lastIndexOf(ch)) != -1){s1 = s1.substring(0,in+1);int length = s1.length();for (int n = 0; n < length/2; n++){if (s1.charAt(n) != s1.charAt(length-n-1)){s1 = s1.substring(0,length-1);continue b;}}st = st.length()>s1.length()?st:new StringBuilder(s1);continue a;}}return st.toString();}

然而效果并没很理想:
第二轮提交效率方面有点提升,内存方面还是和原理差不多,我不是将String原生的截取方法换成了StringBuilder吗?按道理内存方面应该会更优秀一些的,这是为什么呢?难道是因为多次创建StringBuilder对象的原因?

再次优化

这次我就在想:我为什么要进行字符串截取操作呢?这些所有的东西,不都可以用字符串的下标来完成吗?只需要最后在返回最长回文子串的时候截取一次不就行了吗?
又开始改了,主要思路不变,只不过全都改成了对字符串索引进行操作

    public String longestPalindrome(String s){int l;if (s == null || (l =s.length()) < 1) return "";int  a = 0,b = 0,c;a:for (int i = 0; i < l; i++){int e = l-1;char f = s.charAt(i);b:while ((c = s.lastIndexOf(f,e)) != -1 && c != i){e = c-1;for (int n = i,m=0; m < (c-i)/2; n++,m++){if (s.charAt(n+1) != s.charAt(c-1-m)){ continue b; }}if (b - a < c - i){ a = i;b = c; }continue a;}}return s.substring(a,b+1);}

这次有那么一丢丢的进步,不过总体来说还是不是很理想
第三次提交
我觉得这已经是我这个思路的极限了,想要再提升,就应该从优化思路考虑了。

大佬思路:

这题的解法,大致有四种:暴力算法、动态规划、中心扩散、Manacher 算法。
我上面的应该就是暴力算法了,思路清晰,好理解,但是缺点就是效率比较低。
动态规划和中心扩散,作为面试者,一定要掌握。至于Manacher 算法,当扩宽视野就好,鉴于目前的水平,也就不拓宽了。就动态规划和中心扩散吃透就好。

动态规划

没系统的学过这个算法,有点烧脑,借大佬的一张图:
动态规划思路主要思路就是:后一个情况的状态是由前面某个情况的状态确定的。具体的细节可以去看这位大佬写的题解:大佬题解,上面这张图片就是在那里保存的。
看了大佬的思路和代码,我自己再写了一遍:

    public String longestPalindrome(String s){//特判int length;if (s == null || (length = s.length()) < 2){ return s; }int start = 0;int end = 0;boolean[][] dp = new boolean[length][length];char[] chars = s.toCharArray();//对角线肯定为truefor (int i = 0; i < length; i++){ dp[i][i] = true; }for (int j = 1; j < length; j++){for (int i = 0; i < j; i++){if (chars[i] == chars[j] ){if (j- i < 3) { dp[i][j] = true; }else{ dp[i][j] = dp[i+1][j-1]; }}if (dp[i][j] && (j-i) > (end-start) ){start = i;end = j;}}}return s.substring(start,end+1);}

提交结果
提交结果果然比我自己写的那个暴力解法效率高了好几倍。

中心扩散

中心扩散的思路就是:遍历字符串,每对每个字符串进行两遍扩散。然后再记录是否为回文串,记录索引,最后返回最长的那个。我觉得和我自己写那个差不多,不同的就是我是从两边往中间判断,而这个思路是从中间往两头判断,代码就省略了。

总结

自己写了一遍之后,对动态规划有了一点认识。我决定将这个动态规划好好练一练,那个图里,大佬总结了哪些题用到了动态规划。

这篇关于阿亮的算法之路——5. 最长回文子串的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/578507

相关文章

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

关于最长递增子序列问题概述

《关于最长递增子序列问题概述》本文详细介绍了最长递增子序列问题的定义及两种优化解法:贪心+二分查找和动态规划+状态压缩,贪心+二分查找时间复杂度为O(nlogn),通过维护一个有序的“尾巴”数组来高效... 一、最长递增子序列问题概述1. 问题定义给定一个整数序列,例如 nums = [10, 9, 2