阿亮的算法之路——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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

poj3261(可重复k次的最长子串)

题意:可重复k次的最长子串 解题思路:求所有区间[x,x+k-1]中的最小值的最大值。求sa时间复杂度Nlog(N),求最值时间复杂度N*N,但实际复杂度很低。题目数据也比较水,不然估计过不了。 代码入下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring

spoj705( 求不相同的子串个数)

题意:求串s的不同子串的个数 解题思路:任何子串都是某个后缀的前缀,对n个后缀排序,求某个后缀的前缀的个数,减去height[i](第i个后缀与第i-1 个后缀有相同的height[i]个前缀)。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstrin

csu1328(近似回文串)

题意:求近似回文串的最大长度,串长度为1000。 解题思路:以某点为中心,向左右两边扩展,注意奇偶分开讨论,暴力解即可。时间复杂度O(n^2); 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>#include<string>#inclu

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO