最长回文子串问题-Manacher算法【建议收藏】(2)

前面我们讲过一个关于字符串的算法:KMP算法。今天我们来讲另外一个字符串算法:Manacher算法。这个算法是用于解决一个问题叫:最长回文子串

前期文章:KMP算法

牛客网OJ链接

image-20210926152653053

说的简单一点,给定一个字符串,返回的值是这个字符串的最长回文子串的长度。顾名思义,即是回文串,也是子串。

文章目录

  • 一、BF算法

  • 二、Manacher算法

一、BF算法

===============================================================

那上图的示例2为例:abc1234321ab。

最简单的思路就是从左到右遍历每一个字符。每来到一个字符位置,我们可以向左右两边进行扩展,分别比较左右两边的字符。如果相等,就继续向两边扩展;如果不相等,就停止,计算以当前字符,向两边扩展出的长度,就是以当前字符为中心的回文子串。比如:

img

imgimg

就像上图这样,从左往右依次遍历即可。

上面这种思路确实能够解题,但是还有一个很重要的点,那就是假设给定的字符串是偶数个字符,那么这种方式就会错过一些回文子串的匹配,因为此时对于偶数个字符来说,对称点是在中间两个字符之间的,如下图:

img

所以以每个字符为中心点,向两边扩展,还是存在着一定的问题。如何解决呢?

那就是将原字符串进行处理,加工为一个含有特殊字符的字符串,比如原字符串为:123321,;加工后的字符串为:#1#2#3#3#2#1#;

也就是说,在每个字符的中间,加入其它字符,这样就能使一个偶数个字符的字符串,转换为奇数个字符的字符串。这样就可以遍历,向两边扩展了。

问题:我们所加入的字符,必须是原字符中没有的字符吗?

这个问题留作大家思考。

public static int getLengthOfSubString(String str) {

if (str == null) {

return 0;

}

char[] generateStr = generateString(str);

int length = generateStr.length;

int max = 0; //答案

for (int i = 0; i < length; i++) {

int tmp = 1; //每个字符都能以自己本身的字符作为回文子串。所以初始值是1

int radius = 1; //回文半径,也就是以i位置为中心,半径radius的范围内

while (i - radius >= 0 && i + radius < length) { //左右两边都在数组的范围内,循环继续

if (generateStr[i - radius] == generateStr[i + radius]) {

tmp += 2; //左右两个字符相等的情况

radius++; //回文半径加1

} else {

break;

}

}

max = Math.max(max, tmp); //判断当前的tmp是否是最长的回文子串

}

return max / 2; //因为我们比较的处理后的字符串,计算出的回文串要除以2.才是最终的答案

}

public static char[] generateString(String str) {

char[] res = new char[str.length() * 2 + 1]; //原2倍长度,再加1

int index = 0;

for (int i = 0; i < res.length; i++) {

//奇数位置放#,偶数位置放原字符

res[i] = (i % 2) == 1? str.charAt(index++) : ‘#’;

}

return res;

}

以上代码就是BF算法,暴力解。每个字符都需要遍历一次,而每次字符都需要向外扩展,最坏情况下,就是向外扩展一直到整个字符串结束。比如:1111111; 这种情况就是每个字符向外扩展,都会扩展很长,甚至是扩展至字符串结束,所以这个BF算法的时间复杂度是O(N2) 。

二、Manacher算法

=====================================================================

Manacher算法也是在BF算法的基础之上,做了优化。所以大家看Manacher算法之前,先理解BF暴力解的流程。

Manacher算法引入了三个概念:

  • 当前回文子串的中心点 :C

  • 当前已经遍历到最长回文子串的最右边界下标:R

  • 回文半径数组;(用于存储已经扩展完成的回文子串的半径)

通过上面三个变量,我们就能解决这一难题了。话不多说,讲述推导过程。整体分为两个大步骤。C和R的初始值都是-1,也就是数组最左边的外面。

  1. 当i位置(当前遍历的字符)不在R(最右边界)内时

img

此时这种情况,我们只能向左右两边进行扩展。这个没办法。重要的是第2种情况。

  1. 当i位置(当前遍历的字符)在R(最右边界)内时

image-20210926165520939

以7为中心,向两边扩展出来的回文子串,就是橙色括号圈起来的范围。此时的i就是在R边界的里面。

当我们以中心点为对称点,做出i的对称点,如下图:

img

做出来的对称点,我们就能得到这个点的下标。然后去回文半径数组里查这个下标对应的回文半径,就能得到关于这个对称点的回文子串。例如上图中黑色虚线框中的值

对于黑色虚线框的值,我们又可以分为三种小情况:

  • 黑色虚线框与以C中心点扩展的回文子串压线

压线的情况,就是上图中这种情况,黑色虚线框的左边界与橙色线重合。 根据对称性,因为黑色虚线框的值是回文子串,那么右边以i为中心,也能扩展出回文子串。如下图所示:

img

所以我们可以直接通过对称点i得到已经完成匹配的回文子串。然后我们可以直接从i位置的已经计算好的回文子串外开始扩展。比如:左边值7和右边值1做比较,如果相等,当前回文半径加1,然后继续比较下一对字符。

  • 黑色虚线框的左边界,超过了以C中心点扩展的回文子串的左边界(超出):如下图:

img

对称点i,以它为中心对应的回文子串正如左边的黑色虚线框所示:2,3,4,3,2。此时虚线框已经超出了橙色线的范围,又因为橙色线范围内是一个回文子串。所以我们可以推导出当前i位置,至少有回文子串,就是(R-i)为半径的范围。即上图右边黑色虚线框内。

此时我们只需要在此基础之上,比较R右边的值5 和 黑色虚线框左边的2,看是否相等。若相等,则再次比较下一对字符。依次类推。

  • 黑色虚线框整体,都是在以C中心点扩展的回文子串的左半部分(即没压线,也没超出):如下图:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

[外链图片转存中…(img-lIKXWtSn-1713732734111)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值