最长回文子串(Longest Palindromic Substring)

转载自  最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法

    子串:小于等于原字符串长度由原字符串中任意个连续字符组成的子序列

    回文:关于中间字符对称的文法,即“aba”(单核)、“cabbac”(双核)等

    最长回文子串:1.寻找回文子串;2.该子串是回文子串中长度最长的。

一、O(n^3)时间复杂度方法——暴力求解

1.思想:

    1)从最长的子串开始,遍历所有该原字符串的子串;

    2)每找出一个字符串,就判断该字符串是否为回文;

    3)子串为回文时,则找到了最长的回文子串,因此结束;反之,则继续遍历。

2.时间复杂度解释:

    遍历字符串子串:嵌套一个循环、O(n^2);   

    判断是否为回文:再次嵌套一个循环、O(n^3)。

3.java代码详解: 

   public static String longestPalindrome(String s) {if(s.length() <= 1)return s;for(int i = s.length();i > 0; i--) {//子串长度for (int j = 0; j <= s.length() - i; j++) {String sub = s.substring(j , i + j);//子串位置int count = 0;//计数,用来判断是否对称for (int k = 0; k < sub.length() / 2; k++) {//左右对称判断if (sub.charAt(k) == sub.charAt(sub.length() - k - 1))count++;}if (count == sub.length() / 2)return sub;}}return "";//表示字符串中无回文子串}

 

二、O(n^2)时间复杂度方法——从中心向外扩散

1.思想:

     1)将子串分为单核和双核的情况,单核即指子串长度为奇数,双核则为偶数;

    2)遍历每个除最后一个位置的字符index(字符位置),单核:初始low = 初始high = index,low和high均不超过原字符串的下限和上限;判断low和high处的字符是否相等,相等则low++、high++(双核:初始high = 初始low+1 = index + 1);

    3)每次low与high处的字符相等时,都将当前最长的回文子串长度与high-low+1比较。后者大时,将最长的回文子串改为low与high之间的;

    4)重复执行2)、3),直至high-low+1 等于原字符串长度或者遍历到最后一个字符,取当前截取到的回文子串,该子串即为最长的回文子串。

2.时间复杂度解释:

   遍历字符:一层循环、O(n-1);

   找以当前字符为中心的最长回文子串:嵌套两个独立循环、O(2n*(n-1)) = O(n^2)。

3.java代码详解:

private static int maxLen = 0;private static String sub = "";public static String longestPalindrome(String s) {if(s.length() <= 1)return s;for(int i = 0;i < s.length()-1;i++){findLongestPalindrome(s,i,i);//单核回文findLongestPalindrome(s,i,i+1);//双核回文}return sub;}public static  void findLongestPalindrome(String s,int low,int high){while (low >= 0 && high <= s.length()-1){if(s.charAt(low) == s.charAt(high)){if(high - low + 1 > maxLen){maxLen = high - low + 1;sub = s.substring(low , high+1);}low --;//向两边扩散找当前字符为中心的最大回文子串high ++;}elsebreak;}}

 

三、O(n)时间复杂度方法——Manacher算法

1.思想:

    1)将原字符串S的每个字符间都插入一个永远不会在S中出现的字符(本例中用“#”表示),在S的首尾也插入该字符,使得到的新字符串S_new长度为2*S.length()+1,保证Len的长度为奇数(下例中空格不表示字符,仅美观作用);

        例:S:       a  a  b  a  b  b  a

        S_new:        #  a  #  a  #  b  #  a  #  b  #  b  #  a  #

    2)根据S_new求出以每个字符为中心的最长回文子串的最右端字符距离该字符的距离,存入Len数组中,(i为回文中间位置,r为回文右端位置)即S_new[i]—S_new[r]为S_new[i]的最长回文子串的右段(S_new[2i-r]—S_new[r]为以S_new[i]为中心的最长回文子串),Len[i] = r - i + 1;

        S_new:        #  a  #  a  #  b  #  a  #  b  #  b  #  a  #

        Len:            1  2  3  2  1   4  1  4  1  2  5   2  1  2  1

        Len数组性质:Len[i] - 1即为以Len[i]为中心的最长回文子串在S中的长度。在S_new中,以S_new[i]为中心的最长回文子串长度为2Len[i] - 1,由于在S_new中是在每个字符两侧都有新字符“#”,观察可知“#”的数量一定是比原字符多1的,即有Len[i]个,因此真实的回文子串长度为Len[i] - 1,最长回文子串长度为Math.max(Len) - 1。

    3)Len数组求解(线性复杂度(O(n))):

       a.遍历S_new数组,i为当前遍历到的位置,即求解以S_new[i]为中心的最长回文子串的Len[i];

       b.设置两个参数:sub_midd = Len.indexOf(Math.max(Len)表示在i之前所得到的Len数组中的最大值所在位置、sub_side = sub_midd + Len[sub_midd] - 1表示以sub_midd为中心的最长回文子串的最右端在S_new中的位置(原文作者算法思想错误,sub_midd是最右回文的右边界)。起始sub_midd和sub_side设为0,从S_new中的第一个字母开始计算,每次计算后都需要更新sub_midd和sub_side;

      c.当i < sub_side时,取i关于sub_midd的对称点j(j = 2sub_midd - i,由于i <= sub_side,因此2sub_midd - sub_side <= j <= sub_midd);当Len[j] < sub_side - i时,即以S_new[j]为中心的最长回文子串是在以S_new[sub_midd]为中心的最长回文子串的内部,再由于i、j关于sub_midd对称,可知Len[i] = Len[j];

    当Len[j] >= sub.side - i时说明以S_new[i]为中心的回文串可能延伸到sub_side之外,而大于sub_side的部分还没有进行匹配,所以要从sub_side+1位置开始进行匹配,直到匹配失败以后,从而更新sub_side和对应的sub_midd以及Len[i];


        d.当i > sub_side时,则说明以S_new[i]为中心的最长回文子串还没开始匹配寻找,因此需要一个一个进行匹配寻找,结束后更新sub_side和对应的sub_midd以及Len[i]。

2.时间复杂度解释:

        算法只有遇到还没匹配的位置时才进行匹配,已经匹配过的位置不再进行匹配,因此大大的减少了重复匹配的步骤,对于S_new中的每个字符只进行一次匹配。所以该算法的时间复杂度为O(2n+1)—>O(n)(n为原字符串的长度),所以其时间复杂度依旧是线性的。

3.java代码详解:

public String longestPalindrome(String s) {List<Character> s_new = new ArrayList<>();for(int i = 0;i < s.length();i++){s_new.add('#');s_new.add(s.charAt(i));}s_new.add('#');List<Integer> Len = new ArrayList<>();String sub = "";//最长回文子串int sub_midd = 0;//表示在i之前所得到的Len数组中的最大值所在位置int sub_side = 0;//表示以sub_midd为中心的最长回文子串的最右端在S_new中的位置Len.add(1);for(int i = 1;i < s_new.size();i++){if(i < sub_side) {//i < sub_side时,在Len[j]和sub_side - i中取最小值,省去了j的判断int j = 2 * sub_midd - i;if(j >= 2 * sub_midd - sub_side &&  Len.get(j) <= sub_side - i){Len.add(Len.get(j));}elseLen.add(sub_side - i + 1);}else//i >= sub_side时,从头开始匹配Len.add(1);while( (i - Len.get(i) >= 0 && i + Len.get(i) < s_new.size()) && (s_new.get(i - Len.get(i)) == s_new.get(i + Len.get(i))))Len.set(i,Len.get(i) + 1);//s_new[i]两端开始扩展匹配,直到匹配失败时停止//原文作者算法思想错误,这里应该是Len.get(i) + i - 1 >= Len.get(sub_midd) + sub_midd - 1,求最右端回文边界if(Len.get(i) >= Len.get(sub_midd) ){//匹配的新回文子串长度大于原有的长度sub_side = Len.get(i) + i - 1;sub_midd = i;}}sub = s.substring((2*sub_midd - sub_side)/2,sub_side /2);//在s中找到最长回文子串的位置return sub;}


 

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

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

相关文章

小白入门级的视频剪辑软件

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注公众号【雄雄的小课堂】。前言昨天&#xff0c;给班级群里面分享了个视频剪辑软件——剪映PC端&#xff0c;并交代同学们&#xff0c;剪辑视频可以以它来入手&#xff0c;我大致的看了看&#xff0c;PC端的剪映功能基本上能满足…

DDD理论学习系列(12)-- 仓储

1. 引言 DDD中Repository这个单词&#xff0c;主要有两种翻译&#xff1a;资源库和仓储&#xff0c;本文取仓储之译。 说到仓储&#xff0c;我们肯定就想到了仓库&#xff0c;仓库一般用来存放货物&#xff0c;而仓库一般由仓库管理员来管理。当工厂生产了一批货物时&#xf…

Windows 通过 SecureCRT 8.x 上传文件到Linux服务器

转载自 Windows 通过 SecureCRT 8.x 上传文件到Linux服务器 1、SecureCRT 连接 Linux 服务器&#xff0c;这一步操作简单&#xff1a; 2、连接并登录成功后&#xff0c;直接在连接成功的页签上 右键 -> Connect SFTP Session 打开SFTP窗口&#xff1a; 3、在新的SFTP页签…

没有回家的等于没过年

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注公众号【雄雄的小课堂】。今天是农历2021年正月初二&#xff0c;新的一年&#xff0c;祝大家牛年大吉&#xff0c;身体健康&#xff0c;万事如意&#xff01;今年由于各方面原因并未回家过年&#xff0c;感觉没回家等于没过年&a…

.NET Core引入性能分析引导优化

“性能分析引导优化&#xff08;Profile Guided Optimization&#xff0c;缩写PGO&#xff09;”是一项原生编译技术&#xff0c;可用于生成高度优化的代码。它通过一个两步编译过程实现优化——用第一步记录相关执行信息&#xff0c;然后第二步用那些信息构建一个改进过的二进…

手把手教你搭建Maven项目

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。今天给大家分享的是“手把手教你买基金”&#xff0c;奥&#xff01;&#xff01;不对&#xff0c;不好意思&#xff0c;是“手把手教你使用idea搭建Maven项目”&#xff01;前言大家都知道&#x…

实例!使用Idea创建SSM框架的Maven项目

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。前言书接前文&#xff0c;昨天带着大家下载且配置了maven&#xff0c;以及在idea中创建maven项目&#xff0c;今天&#xff0c;我们就结合ssm框架&#xff0c;在idea中实现一个增删改查案例。昨天的…

如何给你的Idea换一个好看漂亮的主题

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。前言大家都知道&#xff0c;idea里面自带两种主题样式&#xff0c;一种是灰色的&#xff0c;另一种是黑色的。你有没有想过换个其他的主题颜色用用呢&#xff1f;如果想&#xff0c;那么今天&#…

腾讯游戏使用Microsoft Service Fabric案例

本文转载自公众号-云就该这么玩&#xff0c;微软官方的地址链接&#xff1a;https://customers.microsoft.com/en-us/story/tencent-telecommunications-azure-service-fabric-windows-server-en 根据研究公司Newzoo的报告估计&#xff0c;全球游戏收入在2017年将达到1080亿美…

div中的table内容过多时不超出div的范围解决方法

转载自 div中的table内容过多时不超出div的范围解决方法 问题描述&#xff1a; 在做界面展示时&#xff0c;table的内容过多&#xff0c;超出div的大小&#xff0c;看着table的边框盖过了div的边框&#xff0c;不美观。 问题解决&#xff1a; 因为页面整体的布局美工都设计…

属性编辑器未在PropertyEditorManager中注册?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。前言好不容易将代码都写完了&#xff0c;结果一运行&#xff0c;报错&#xff01;&#xff01;HTTP Status 500 - Unable to convert string "${book.btime}" to class "java.util.…

DDD理论学习系列(13)-- 模块

1. 引言 Module&#xff0c;即模块&#xff0c;是指提供特定功能的相对独立的单元。提到模块&#xff0c;你肯定就会想到模块化设计思想&#xff0c;也就是功能的分解和组合。对于简单问题&#xff0c;可以直接构建单一模块的程序。而对于复杂问题&#xff0c;则可以先创建若干…

快速搭建Springboot项目的两种方式!!

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。前言Springboot的特点就是简单、快速和方便&#xff0c;使用idea不到一分钟就可以快速搭建springboot项目&#xff0c;并且&#xff0c;在这里&#xff0c;你不用写spring的那些乱七八糟的xml文件&…

简化springboot部署,太灵活方便了!

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。前言Springboot项目的编写与开发想必大家都会了&#xff0c;是不是和我们之前的项目比起来&#xff0c;简单方便了许多&#xff1f;今天&#xff0c;我们来看看除了可以直接在idea中部署和运行项目…