怎么判断一个字符串的最长回文子串是否在头尾_LeetCode 5 迅速判断回文串的Manacher算法...

e7e6b6d6cf9187a99adc972e1c2907e7.png

本文始发于个人公众号: TechFlow

题意

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Link: https://leetcode.com/problems/longest-palindromic-substring/

翻译

给定一个字符串s,要求它当中的最长回文子串。可以假设s串的长度最大是1000。

样例

Example 1:
Input: "babad"Output: "bab"Note: "aba" is also a valid answer.Example 2:
Input: "cbbd"Output: "bb"

分析

虽然LeetCode里给这道题的难度是Medium,但实际上并不简单,我们通过自己思考很难想到最佳解法。
我们先把各种算法放在一边,先从最简单的方法开始。最简单的方法当然是暴力枚举,但是这道题和之前的字符串问题不同。

我们在暴力枚举的时候,并不需要枚举所有的起始位置,再判断这个子串是否回文。实际上我们可以利用回文串两边相等的性质,直接枚举回文串的中心位置,如果两边相等就往两边延伸。这样我们最多需要枚举n个回文中心,每次枚举最多遍历n次。所以最终的复杂度是O(n^2)。


有经验的同学看到这个复杂度就能反应过来,这明显不是最优解法。但是对于当前问题,暴力枚举固然不是最佳解法,但其实也算得上是不错了,并没有我们想的那么糟糕,不信的话,我们来看另一个看起来高端很多的解法。

动态规划(DP)

这道题中利用回文串的性质还有一个trick,对于一个字符串S,如果我们对它进行翻转,得到S_,显然它当中的回文子串并不会发生变化。所以如果我们对翻转前后的两个字符串求最长公共子序列的话,得到的结果就是回文子串。

算法导论当中对这个问题的讲解是使用动态规划算法,即是对于字符串S中所有的位置i和S_中所有的位置j,我们用一个dp数组记录下以i和j结尾的S和S_的子串能够组成的公共子序列的最大的结果。

显然,对于i=0,j=0,dp[i][j] = 0(假设字符串下标从1开始)

我们写出DP的代码:

for 


我们不难观察出来,这种解法的复杂度同样是O(n^2)。并且空间复杂度也是O(n^2),也就是说我们费了这么大劲,并没有起到任何优化。所以从这个角度来看,暴力搜索并不是这题当中很糟糕的解法。

分析到了这里,也差不多了,下面我们直接进入正题,这题的最佳解法,O(n)时间内获取最大回文子串的曼彻斯特算法。

曼切斯特算法


回文串除了我们刚刚提到的性质之外,还有一个性质,就是它分奇偶。简而言之,就是回文串的长度可以是奇数也可以是偶数。如果是奇数的话,那么回文串的回文中心就是一个字符,如果是偶数的话,它的回文中心其实是落在两个字符中间。


举个例子:


ABA和ABBA都是回文串,前者是奇回文,后者是偶回文


这两种情况不一致,我们想要一起讨论比较困难,为了简化问题,我们需要做一个预处理,将所有的回文串都变成奇回文。怎么做呢,其实很简单,我们在所有两个字符当中都插入一个特殊字符#。


比如:


abba -> #a#b#b#a#


这样一来,回文中心就变成中间的#了。我们再来看原本是奇回文的情况:


aba -> #a#b#a#


回文中心还是在b上,依然还是奇回文。


预处理的代码:

def 

曼切斯特算法用到三个变量,分别是数组p,idx和mr。我们接下来一个一个介绍。

首先是数组radis,它当中存在的是每个位置能构成的最长回文串的半径。注意,这里不是长度,是半径

我们举个例子:

字符串S     # a # b # b # a #
radis      1 2 1 2 5 2 1 2 1


我们先不去想这个radis数组应该怎么求,我们来看看它的性质。


首先,i位置的回文串的半径是radis[i],那么它的长度是多少?很简单: radis[2] * 2 - 1。那么,这个串中去掉#之后剩下的长度是多少?也就是说预处理之前的长度是多少?


答案是radis[i] - 1,推算也很简单,总长度是radis[i] * 2 - 1,其中#比字母的数量多一个,所以原串的长度是(radis[i] * 2 - 1 - 1)/2 = radis[i] - 1。


也就是说原串的长度和radis数组就算是挂钩了。


idx很好理解,它就是指的是数组当中的一个下标,最后是mr,它是most_right的缩写。它记录的是在当前位置i之前的回文串所向右能延伸到的最远的位置。


听起来有些拗口,我们来看个例子:

ae291d7ed28091fcb5800b5d62a97cfe.png

此时i小于mr,mr对应的回文中心是id。那么i在id的回文范围当中,对于i而言,我们可以获取到它关于id的对称位置:id * 2 - i,我们令它等于i_。知道这个对称的位置有什么用呢?很简单,我们可以快速的确定radis[i]的下界。在遍历到i的时候,我们已经有了i_位置的结果。通过i_位置的结果,我们可以推算i位置的范围。

radis[i] >= min(radis[i_], mr-i)

为什么是这个结果呢?

我们把情况写全,假设mr-i > radis[i_]。那么i_位置的回文串全部都落在id位置的回文串里。这个时候,我们可以确定radis[i]=radis[i_]。为什么呢?

因为根据对称原理,如果以i为中心的回文串更长的话,我们假设它的长度是radis[i_]+1。会导致什么后果呢?如果这个发生,那么根据关于id的对称性,这个字符串关于id的对称位置也是回文的。那么radis[i_1]也应该是这么多才对,这就构成了矛盾。如果你从文字描述看不明白的话,我们来看下面这个例子:

S:       c a b c b d b c b a 
cradis:    x_  i_  5   i   x

在这个例子当中,mr-i=5,radis[i_]=2。所以mr - i > radis[i_]。如果radis[i]=3,那么x的位置就应该等于id的位置,同理根据对称性,x_的位置也应该等于id的位置。那么radis[i_]也应该是3。这就和它等于2矛盾,所以这是不可能出现的,在mr距离足够远的情况下,radis[i_]的值限制了i位置的可能性。


我们再来看另一种情况,如果mr - i < radis[i_]时会怎么样呢?


在这种情况下,由于mr距离i太近,导致i对称位置的半径无法在i位置展开。但是mr的右侧可能还存在字符,这些字符可以构成新的回文吗?

字符串S     XXXXXXXXSXXXXXXXXXXXXXXX
radis        i_    id    i mr


也就是说S[mr+1]会和S[i*2-mr-1]的位置相同吗?
其实我们可以不用判断就可以知道答案,答案是不会。
我们来看图:

a30f5d4cac12c40cb0a6e6bffde42eab.png


根据对称性,如果mr+1的位置对于i可以构成新的对称。由于radis[i_] > mr-i,也就是说对于i_位置而言,它的对称范围能够辐射到mr对称点的左边。我们假设这个地方的字母是a,根据对称性,我们可以得出mr+1的位置也应该是a。如此一来,这两个a又能构成新的对称,那么id位置的半径就可以再拓展1,这就构成了矛盾。所以,这种情况下,由于mr-i的限制,使得radis[i]只能等于mr - i。

那什么情况下i位置的半径可以继续拓展呢?


只有mr - i == radis[i_]的时候,id构成的回文串的左侧对于i_可能构不成新的回文,但是右侧却存在这种可能性。

3d8d76bde41e7a86af04ef6f699ff0c0.png

在上图这个例子当中,i_的位置的回文串向左只能延伸到ml,因为ml-1的位置和关于i_对称的位置不相等。对于mr的右侧,它完全可以既和i点对称,又不会影响raids[id]的正确性。这个时候,我们就可以通过循环继续遍历,拓展i位置的回文串。

整个过程的分析虽然很多,也很复杂,但是写成代码却并不多。

# 初始化

到这里,曼切斯特算法就算是实现完了。虽然我们用了这么多篇幅去介绍它,可是真正写出来,它只有几行代码而已。不得不说,实在是非常巧妙,第一次学习可能需要反复思考,才能真正理解。

不过我们还有一个问题没有解决,为什么这样一个两重循环的算法会是O(n)的复杂度呢?

想要理解这一点,需要我们抛开所有的虚幻来直视本质。虽然我们并不知道循环进行了多少次,但是有两点可以肯定。通过这两点,我们就可以抓到复杂度的本质。

第一点,mr是递增的,只会变大,不会减小。

第二点,mr的范围是0到n,每次mr增加的数量就是循环的次数。

所以即使我们不知道mr变化了多少次,每次变化了多少,我们依然可以确定,这是一个O(n)的算法。

到这里,文章的内容就结束了,如果喜欢的话,请点个关注吧~

c5380a0842945b334af0724c39d1a1b0.png

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

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

相关文章

linux内核没有iobuf,LINUX2.6.26.6内核下双口RAM的驱动函数测试成功!

驱动程序&#xff1a;#include //#include #include #include #include #include #include #include //#include #include //#include #include #include #include #include //#include #include #include #include #include #include #include #include MODULE_LICENSE("…

spring和mybatis结合做简单的增删查改系统_springbootamp;amp;vue简单的景点信息管理系统...

springboot&&vue简单的景点信息管理系统这两天闲着没有什么事&#xff0c;就根据陈哥的教程&#xff0c;试着写了一个springboot和vue的简单的景点信息管理系统。也就大致实现了最基本的增删查改。先看看效果图吧&#xff1a;1、登陆界面&#xff1a; 2、注册界面&…

linux 内核 丢弃分片包,LINUX内核关于IP分片重组问题请教

最近研究学习IP分片重组&#xff0c;也拜读了不少dx的阅读理解。可还是有疑问&#xff0c;请教xdm。源代码&#xff1a;linux-2.4.26\linux-2.4.26\net\ipv4\ip_fragment.cIP分片的重组大概经过以下几个函数:0/ ip_defrag1/ ip_find-->ip_frag_create-->ip_frag_intern2/…

spark算子_十、Spark之详解Action类算子

常用Action类算子列表reduce(func): 通过func函数来对RDD中所有元素进行聚合运算&#xff0c;先运算分区内数据&#xff0c;再运算分区间数据。scala> val rdd1 sc.makeRDD(1 to 100)rdd1: org.apache.spark.rdd.RDD[Int] ParallelCollectionRDD[4] at makeRDD at :24# 对…

linux 库函数 劫持,Linux hook技术之-Ring3下动态链接库.so函数劫持

劫持普通函数当然没有什么意思了&#xff01;我们要劫持的是系统函数&#xff01;我们知道&#xff0c;Unix操作系统中对于GCC而言&#xff0c;默认情况下&#xff0c;所编译的程序中对标准C函数(fopen、printf、execv家族等等函数)的链接&#xff0c;都是通过动态链接方式来链…

await原理 js_「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能

本周&#xff0c;Nodejs v14.3.0 发布。这个版本包括添加顶级 Await、REPL 增强等功能。REPL 增强通过自动补全改进对 REPL 的预览支持&#xff0c;例如&#xff0c;下图中当输入 process.ver 之后&#xff0c;不需要输入剩下的实际内容&#xff0c;它帮我们生成了自动补全的输…

在linux安装requests库命令,在Linux--Ubuntu18.04环境下安装requests库

之前在服务器上装过requests库&#xff0c;但是记忆中花了好大的力气才成功&#xff0c;现在因为一次意外&#xff0c;服务器重装系统&#xff0c;现在这些乱七八糟的库又要重装一遍&#xff0c;与上次不同的是&#xff0c;这次我装一遍就成功了。现在分享一下成功的经历。Pyth…

linux输入ls后不显示_零基础学习之Linux基础命令小结

安装完重启后&#xff0c;没有像sery所说在图形界面崩溃了&#xff0c;由于我没有安装X-WINDOWS而是直接进入了文本界面。如果你想做linux管理的话&#xff0c;最好在文本界面下工作&#xff0c;这样会适应如下图:第一行显示的是我们所安装的linux是Red Hat 企业4第二行显示的是…

redhat enterprise linux 哪个版本好,Red Hat Enterprise Linux 版本显示中(Santiago)是啥意思?...

楼主的逻辑还有问题。1、linux跟windows都是一种操作系统&#xff0c;但是它用的分区格式是ext3的&#xff0c;ntfs和fat都不合适。安装过程中你可以自己选择删除现有分区创建新分区&#xff0c;但如果你不了解&#xff0c;很可能把所有的分区都清了。2、redhat分区多大合适看你…

.gitignore文件_【第1739期】为Git仓库里的.idea文件夹正名

前言.idea该不该提交到代码仓库中呢&#xff1f;你的意见呢&#xff1f;今日早读文章由《Flask Web开发》作者李辉分享。正文从这开始&#xff5e;&#xff5e;在网络上&#xff0c;我曾多次看到人们对于Git仓库中的.idea文件夹的偏见。最近的一次是在某个博客中技术专家对于志…

监控linux时间不对,shell 计算故障时间 配合web监控

#!/bin/bash#checkfail.log 为SHELL监控网站时间存放的日志文件 https://blog.51cto.com/junhai/2437965fail_time(){starttimetail -n 1000 checkfail.log |grep "$url"|grep "第1次"|tail -n 3|head -n 1|awk {print $1, $2} #取网站挂掉的时间endtimet…

linux redis清空数据恢复,Redis数据恢复--误删数据后一次吓尿的经历

1、起因&#xff0c;一个flushdb命令因为误操作&#xff0c;输入了一个flushdb命令&#xff0c;导到redis里0号库里的数据全部清空&#xff0c;OMG&#xff0c;这里有不少重要信息&#xff0c;如果被领导知道&#xff0c;必开除2、appendonly留有生机仔细想想&#xff0c;当时数…

c语言 枚举类型 uint32_浅谈C语言枚举类型 | 附自创用法分享

经济学家说过&#xff0c;路边是不会有100元的&#xff1b;但如果有&#xff0c;你还是要捡起来。同理&#xff0c;在貌似万物免费的网络时代&#xff0c;你是很难找到有针对性的好资料&#xff1b;但是如果有&#xff0c;希望你能认真学习吸收。比如笔者今天写的这一篇一今天这…

linux在bin下加入ssh,移植 ssh 到开发板

2》编译/home/arm下新建目录sshwork&#xff0c;并且将源码复制到该目录下mkdir /home/arm/sshworkcp zlib-1.2.3.tar.gz openssl-0.9.8d.tar.gz openssh-4.6p1.tar.gz/home/arm/sshwork/home/arm/sshwork下新建目录lib&#xff0c;用来保存生成的库文件。mkdir /home/arm/sshw…

java pdf增删改查_如何利用Java代码操作索引库?

今天是刘小爱自学Java的第161天。感谢你的观看&#xff0c;谢谢你。学习计划安排如下&#xff1a;学了几天的Elasticserch&#xff0c;但都是它本身的知识点&#xff0c;如何通过Java语言去操作它呢&#xff1f;这就好比以前学数据库&#xff0c;在数据库工具中通过sql语句也能…

linux shell 第几行,Linux shell 获得字符串所在行数及位置

shell 获得字符串所在行数及位置01 获取字符串所在的行数方式一&#xff1a;用grep -n[rootroot]# cat testapplebitcreatedelectexeflowgood[rootroot]# cat test | grep -n exe5:exe[rootroot]# cat test | grep -n exe | awk -F ":" {print $1}5方式二&#xff1a…

sublime text3 怎么配置、运行python_SublimeText3按ctrl+b执行python无反应

最后更新时间&#xff1a;2017-09-14 现象&#xff1a; 在Sublime中打开.py文件&#xff0c;按”ctrlb”执行时无反应。点击工具->编译系统中已经有且识别到Python&#xff0c;但执行”run&#xff08;ctrlshiftb&#xff09;”时无反应&#xff0c;Sublime左下角提示”No B…

linux 火锅平台,“定制版火锅”来袭,持续创新才能永葆活力

原标题&#xff1a;“定制版火锅”来袭&#xff0c;持续创新才能永葆活力5月1日&#xff0c;重庆涪陵红酒小镇的一家转转火锅店&#xff0c;推出“五一”定制版火锅免费请游客品尝。广西的螺蛳粉、贵州的折耳根、湖南臭豆腐、福建乌龙茶、重庆榨菜、河南胡辣汤、陕西老陈醋、海…

internetreadfile读取数据长度为0_YOLOV3的TensorFlow2.0实现,支持在自己的数据集上训练...

GitHub链接&#xff1a;calmisential/YOLOv3_TensorFlow2​github.com我主要参考了yolov3的一个keras实现版本&#xff1a;qqwweee/keras-yolo3​github.com目前支持在PASCAL VOC 2012数据集上训练和自定义数据集上训练&#xff0c;具体的训练过程可参考项目仓库中的README文档…

c语言用链表对学生成绩排序,学生成绩排序和平均分计算利用c语言链表的创建插入删除.doc...

#define NULL 0#define LEN sizeof(struct student)struct student{long num;float score;struct student *next;};int n;struct student *creat(void)//创建链表{struct student *head;struct student *p1,*p2;n0;p1p2(struct student*)malloc(LEN);scanf("%ld,%f",…