怎么判断一个字符串的最长回文子串是否在头尾_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,一经查实,立即删除!

相关文章

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

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

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# 对…

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输入ls后不显示_零基础学习之Linux基础命令小结

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

.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;希望你能认真学习吸收。比如笔者今天写的这一篇一今天这…

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

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

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…

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文档…

深井软岩巷道群支护技术与应用_深井软岩巷道深浅孔帷幕注浆技术

一、成果内容1.基本原理对失修巷道进行刷扩、支护&#xff0c;满足使用断面后进行帮顶喷浆、底板整平&#xff0c;先底板注浆&#xff0c;然后帮、顶注浆。锚架充支护巷道直接底板整平后&#xff0c;先底板后帮、顶注浆。通过全断面深浅孔联合注水泥浆进行巷道加固&#xff0c;…

店铺咨询系统c语言,课内资源

1 题目介绍1.1 问题描述出于不同目的的旅客对交通工具和交通路径有不同的要求。例如,因公出差的旅客希望在旅途中的时间尽可能短,出门旅游的游客则期望旅费尽可能省,而老年旅客则要求中转次数最少。编制一个交通咨询系统程序,为旅客提供最优决策的交通咨询。1.2 需求分析提供对…

f分布表完整图_【教育统计答疑】如何理解正态分布、均值分布、^2分布、t分布和F分布...

许多教育统计的初学者都表示这几个分布感到学起来非常吃力&#xff0c;结合最近上课的体会以及答疑的情况&#xff0c;觉得很有必要在这里简单地对这部分内容进行澄清和梳理&#xff0c;以助理解。首先&#xff0c;“为什么要学习这几个分布”可能是许多人纠结的问题&#xff0…

新颖的c语言题目,新颖版c语言经典习题100例(全面面)

新颖版c语言经典习题100例(全面面) (66页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;19.90 积分&#xfeff;实用文档C语言习题100例(最新整理版)习题目录&#xff1a;(按住Ctrl点击可以快速跳转到…

c语言jt808协议库,平台如何应答——关于JT/T808协议

前两篇也说明了一些应答的相关内容&#xff0c;对于刚接触的开发者来说恐怕还是不太容易理解&#xff0c;这里专门列举一个篇幅来讲解如何针对终端设备上报的信息进行应答。严格来讲&#xff0c;如果不应答&#xff0c;终端设备会判别为服务平台断开连接&#xff0c;就会重复发…

熊猫的python小课账号_学习python中的pandas有没有好的教程推荐?

之前好多人私信我python数据分析怎么快速入门&#xff0c;我在这里直接介绍一下自己的心得经验吧。 要学习pandas&#xff0c;我并不建议看大量的教程&#xff0c;等看完教程&#xff0c;天都黑了&#xff0c;一觉醒来热情都凉了。 我的建议是&#xff0c;首先放平心态&#xf…

ubuntu nfs linux,Ubuntu的NFS功能配置

环境:Ubuntu 10.04步骤:1.sudo apt-get install portmap2.sudo apt-get install nfs-kernel-server注意:第2步我安装失败,换源后仍然如此,最后我在新立得里面搜索到nfs-kernel-server,安装成功3.在更目录下新建共享目录:mkdir /forlinux4.gedit /etc/exprots,在后面添加/forlin…

一个android工程生成两个aar,android studio生成aar包并在其他工程引用aar包(示例代码)...

1.aar包是android studio下打包android工程中src、res、lib后生成的aar文件&#xff0c;aar包导入其他android studio 工程后&#xff0c;其他工程可以方便引用源码和资源文件2.生成aar包步骤&#xff1a;①.用android studio打开一个工程&#xff0c;然后新建一个Module&#…

android 尺寸变化动画,Android ScaleAnimation类:尺寸变化动画类

ScaleAnimation类是Android系统中的尺寸变化动画类&#xff0c;用于控制View对象的尺寸变化&#xff0c;该类继承于Animation类。 ScaleAnimation类中的很多方法都与Animation类一致&#xff0c;该类中最常用的方法便是ScaleAnimation构造方法。【基本语法】public ScaleAnimat…