回文串相关算法的总结

1. 题型

  • 最长回文子串
  • 回文子串的个数

2. 暴力求解

枚举出所有的子串,然后再判断这些子串是否是回文。假设字符串的长度为 n。我们可以看出前者会用 O ( n 2 ) O(n^2) O(n2) 的时间枚举出所有的子串 s [ l i ⋯ r i ] s[l_i\cdots r_i] s[liri] 然后再用 O ( r i − l i + 1 ) O(r_i-l_i+1) O(rili+1) 的时间检测当前的子串是否是回文,整个算法的时间复杂度是 O ( n 3 ) O(n^3) O(n3)

3. 中心扩展算法

枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同的时候就拓展,否则停止拓展。

枚举回文中心的是 O ( n ) O(n) O(n) 的时间复杂度,对于每个回文中心拓展的次数也是 O ( n ) O(n) O(n) 的,所以时间复杂度是 O ( n 2 ) O(n^2) O(n2)

简单来说就是先确定一个回文中心 mid ,然后向两边扩展,直到扩展不动了,此时窗口中的回文子串即为以 mid 为中心的最长回文子串(最长回文子串)。

同时,确定中心后那个中心即为一个回文子串,每次扩展都会形成一个新的回文子串(回文子串的个数)。

在实现的时候,我们需要处理一个问题,即如何有序地枚举所有可能的回文中心,我们需要考虑回文长度是奇数和回文长度是偶数的两种情况。如果回文长度是奇数,那么回文中心是一个字符;如果回文长度是偶数,那么中心是两个字符。当然你可以做两次循环来分别枚举奇数长度和偶数长度的回文,但是我们也可以用一个循环搞定。我们不妨写一组出来观察观察,假设 n=4,我们可以把可能的回文中心列出来:
在这里插入图片描述
由此我们可以看出长度为 n 的字符串会生成 2n−1 组回文中心 [ l i , r i ] [l_i, r_i] [li,ri],其中 l = i // 2,r = i // 2 + i % 2 (//表示计算完除法向下取整)

例题1:求回文子串的个数

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。示例 1:输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

解:

class Solution:def countSubstrings(self, s: str) -> int:n = len(s)res = 0for i in range(2 * n - 1):l = i // 2r = i // 2 + i % 2while 0 <= l and r < n and s[l] == s[r]:l -= 1r += 1res += 1return res

4. 马拉车算法(Manacher算法)

这个算法将解决回文子串的时间复杂度被提升为 O ( n ) O(n) O(n)

这是一个奇妙的算法,是1957年一个叫Manacher的人发明的,所以叫Manacher‘s Algorithm,主要是用来查找一个字符串的最长回文子串,这个算法最大的贡献是将时间复杂度提升到线性,前面我们说的动态规划的时间复杂度为 O(n2)。

前面说的中心拓展法,中心可能是字符也可能是字符的间隙,这样如果有 n 个字符,就有 n+n+1 个中心:

在这里插入图片描述
为了解决上面说的中心可能是间隙的问题,我们往每个字符间隙插入 “#”,为了让拓展结束边界更加清晰,左边的边界插入 “^” ,右边的边界插入 “$”:

在这里插入图片描述
S 表示插入"#", “^” , "$"等符号之后的字符串,我们用一个数组P表示S中每一个字符能够往两边拓展的长度:

在这里插入图片描述
比如 P[8] = 3,表示可以往两边分别拓展3个字符,也就是回文串的长度为 3,去掉 # 之后的字符串为 “aca” :

在这里插入图片描述
P[11]= 4,表示可以往两边分别拓展4个字符,也就是回文串的长度为 4,去掉 # 之后的字符串为 “caac” :

在这里插入图片描述

假设我们已经得知数组P,那么我们怎么得到回文串?

用 P 的下标 index ,减去P[i](也就是回文串的半径),可以得到回文串开头字符在拓展后的字符串 S 中的下标,除以2,就可以得到在原字符串中的下标了。

那么现在的问题是:如何求解数组P[i]

其实,马拉车算法的关键是:它充分利用了回文串的对称性,用已有的结果来帮助计算后续的结果。

假设已经计算出字符索引位置 P 的最大回文串,左边界是PL,右边界是PR

在这里插入图片描述
那么当我们求位置 i 的值 P[i] 的时候,如果 i 小于等于 PR,其实我们可以找到 i 关于 P 的对称点 j :

在这里插入图片描述
那么假设 j 为中心的最长回文串长度为 len,并且在 PL 到 P 的范围内,则 i 为中心的最长回文串也是如此:以 i 为中心的最长回文子串长度等于以 j 为中心的最长回文子串的长度

但是这里有两个问题:

前一个回文字符串P,是哪一个?
有哪些特殊情况?特殊情况怎么处理?
(1) 前一个回文字符串 P,是指的前面计算出来的右边界最靠右的回文串,因为这样它最可能覆盖我们现在要计算的 i 为中心的索引,可以尽量重用之前的结果的对称性。

也正因为如此,我们在计算的时候,需要不断保存更新 P 的中心和右边界,用于每一次计算。

(2) 特殊情况其实就是当前 i 的最长回文字符串计算不能再利用 P 点的对称,包括以下两种情况:

一是以 i 为中心的回文串的右边界超出了 P 的右边界 PR

在这里插入图片描述

这种情况的解决方案是:超过的部分,需要按照中心拓展法来一一拓展。

二是i 不在 以 P 为中心的回文串里面,只能按照中心拓展法来处理
在这里插入图片描述
至于算法的复杂度,空间复杂度借助了大小为n的数组,为O(n),而时间复杂度,看似是用了两层循环,实则不是 O(n2),而是 O(n),因为绝大多数索引位置会直接利用前面的结果以及对称性获得结果,常数次就可以得到结果,而那些需要中心拓展的,是因为超出前面结果覆盖的范围,才需要拓展,拓展所得的结果,有利于下一个索引位置的计算,因此拓展实际上较少。

实现1:求回文子串的个数

import mathclass Solution:def preProcess(self, s, n):if n == 0:return "^$"ret = "^"for i in range(n):ret += "#" + s[i]ret += "#$"return retdef countSubstrings(self, s: str) -> int:n = len(s)S = self.preProcess(s, n)N = len(S)p = [0] * Ncenter, right = 0, 0res = 0for i in range(1, N-1):j = 2 * center - iif right > i:p[i] = min(p[j], right - i)else:p[i] = 0# 中心扩展,已经确定[i-p[i], i+p[i]]内的子串为回文串了while S[i + p[i] + 1] == S[i - p[i] - 1]:p[i] += 1if i + p[i] > right:center = iright = i + p[i]# 因为在字符串里面插入了#,所以需要除以2# 比如其中的一个回文子串为 "#a#b#c#b#a#",这种情况下p[index_c] = 5# 实际包含的子串为:"c", "bcb", "abcba", 5 / 2 向上取整为3res += math.ceil(p[i] / 2)return ress = "abccb"
solution = Solution()
print(solution.countSubstrings(s))

实现2:求最长回文子串

def preProcess(s):n = len(s)if n == 0:return "^$"ret = "^"for i in range(n):ret += "#" + s[i]ret += "#$"return retdef longestPalindrome(str):S = preProcess(str)n = len(S)# 用于保存回文串的半径P = [0]*n# 用于保存边界最右的回文中心以及右边界center = 0right = 0# 从第 1 个字符开始for i in range(1, n-1):# 找出i关于前面中心的对称,i+j=2*centerj = 2 * center - iif right > i:# i 在右边界的范围内,看看i的对称点的回文串长度,以及i到右边界的长度,取两个较小的那个# 不能溢出之前的边界,否则就得中心拓展P[i] = min(right - i, P[j])else:# 超过范围了,中心拓展P[i] = 0# 中心拓展while S[i + 1 + P[i]] == S[i - 1 - P[i]]:P[i] += 1# 看看新的索引是不是比之前保存的最右边界的回文串还要靠右if i + P[i] > right:# 更新中心center = i# 更新右边界right = i + P[i]# 通过回文长度数组找出最长的回文串maxLen = 0centerIndex = 0for i in range(1, n-1):if P[i] > maxLen:maxLen = P[i]centerIndex = istart = (centerIndex - maxLen) // 2return str[start: start + maxLen]

参考:马拉车算法,其实并不难!!!

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

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

相关文章

ORB-SLAM2 ---- 非线性优化在SLAM中的应用(一)

文章目录 一、为什么要讲非线性优化二、运动模型和观测模型三、最大似然估计四、SLAM中最小二乘的应用五、总结 一、为什么要讲非线性优化 相信大家在学习一段时间SLAM后&#xff0c;会发现两个问题。第一个是代码能看懂&#xff0c;但是不知道为什么这样做&#xff08;特别是优…

论文概览 |《Urban Analytics and City Science》2023.03 Vol.50 Issue.3

本次给大家整理的是《Environment and Planning B: Urban Analytics and City Science》杂志2023年3月第50卷第3期的论文的题目和摘要&#xff0c;一共包括18篇SCI论文&#xff01; 论文1 A new kind of search 一种新型的搜索 【摘要】 ChatGPT (2022) was first launched o…

电子商务人工智能指南 4/6 - 内容理解

介绍 81% 的零售业高管表示&#xff0c; AI 至少在其组织中发挥了中等至完全的作用。然而&#xff0c;78% 的受访零售业高管表示&#xff0c;很难跟上不断发展的 AI 格局。 近年来&#xff0c;电子商务团队加快了适应新客户偏好和创造卓越数字购物体验的需求。采用 AI 不再是一…

4.STM32通信接口之SPI通信(含源码)---软件SPI与W25Q64存储模块通信实战《精讲》

经过研究SPI协议和W25Q64&#xff0c;逐步了解了SPI的通信过程&#xff0c;接下来&#xff0c;就要进行战场实战了&#xff01;跟进Whappy步伐&#xff01; 目标&#xff1a;主要实现基于软件的SPI的STM32对W25Q64存储写入和读取操作&#xff01; 开胃介绍&#xff08;代码基本…

【ArcGISPro】训练自己的深度学习模型并使用

本教程主要训练的是识别汽车的对象检测模型 所使用的工具如下(导出训练数据进行深度学习、训练深度学习模型、使用深度学习检测对象) 1.准备训练数据 1.1新建面矢量,构建检测对象 右键地理数据库->新建->要素类 选择面类型 1.2点击编辑窗口进行勾画汽车检测对象…

NineData云原生智能数据管理平台新功能发布|2024年11月版

本月发布 8 项更新&#xff0c;其中重点发布 2 项、功能优化 6 项。 重点发布 数据库 Devops - 数据生成支持多个数据源 NineData 支持在数据库中自动生成符合特定业务场景的随机数据&#xff0c;用于模拟实际生产环境中的数据情况&#xff0c;帮助用户在不使用真实数据的情况…

RabbitMQ延迟消息的实现

RabbitMQ延迟队列的实现 延迟消息是什么延迟消息的实现死信交换机代码实现 延迟消息插件 延迟消息是什么 延迟消息是将消息发送到MQ中&#xff0c;消费者不会立即收到消息&#xff0c;而是过一段时间之后才会收到消息&#xff0c;进行处理。在一些业务中&#xff0c;可以用到延…

当大的div中有六个小的div,上面三个下面三个,当外层div高变大的时候我希望里面的小的div的高也变大

问&#xff1a; 当大的div中有六个小的div&#xff0c;上面三个下面三个&#xff0c;当外层div高变大的时候我希望里面的小的div的高也变大 回答&#xff1a; 这时候我们就不能写死六个小的div的高度&#xff0c;否则上下的小的div的间距就会变大&#xff0c;因为他们的高度…

L2G3000-LMDeploy 量化部署实践

文章目录 LMDeploy 量化部署实践闯关任务环境配置W4A16 量化 KV cacheKV cache 量化Function call LMDeploy 量化部署实践闯关任务 环境配置 conda create -n lmdeploy python3.10 -y conda activate lmdeploy conda install pytorch2.1.2 torchvision0.16.2 torchaudio2.1.…

企业数字化转型:从爆品起步,迈向生态平台

在当今数字化浪潮席卷全球的时代&#xff0c;企业数字化转型已成为必然趋势。然而&#xff0c;这条转型之路该如何走呢&#xff1f; 企业数字化转型的路径设计&#xff0c;绝不仅仅是技术的升级换代&#xff0c;它需要综合考量多方面因素。一方面&#xff0c;要为实现战略目标做…

如何将快捷指令添加到启动台

如何将快捷指令添加到启动台/Finder/访达&#xff08;Mac&#xff09; 1. 打开快捷指令创建快捷指令 示例创建了一个文件操作测试的快捷指令。 2. 右键选择添加到程序坞 鼠标放在待添加的快捷指令上。 3. 右键添加到访达 鼠标放在待添加的快捷指令上。 之后就可以在启…

怎么实现邮件营销自动化?

邮件营销能够出色地帮助我们与客户建立良好关系。无论是新客户还是老客户&#xff0c;都可以通过邮件来达成较为良好的客户关系。然而&#xff0c;从消费者的角度来看&#xff0c;每个人都有自己独特的习惯和特点&#xff0c;没有人希望收到千篇一律、营销意味过重的邮件。因此…

【优选算法 二分查找】二分查找算法入门详解:二分查找小专题

x 的平方根 题目解析 算法原理 解法一&#xff1a; 暴力解法 如果要求一个数(x)的平方根&#xff0c;可以从 0 往后枚举&#xff0c;直到有一个数(a)&#xff0c;a^2<x&#xff0c;(a1)^2>x&#xff0c;a即为所求&#xff1b; 解法二&#xff1a;二分查找 …

理工男创业方案:一款智能AI久坐提醒器产品的技术实现方案

起身提醒器技术实现方案 随着现代工作方式的改变&#xff0c;越来越多的上班族长时间坐在电脑前&#xff0c;缺乏足够的活动&#xff0c;容易导致各种健康问题&#xff0c;如脊椎病、眼睛疲劳、肌肉酸痛等。因此&#xff0c;设计一款智能起身提醒器&#xff0c;以帮助用户改善…

查询产品所涉及的表有(product、product_admin_mapping)

文章目录 1、ProductController2、AdminCommonService3、ProductApiService4、ProductCommonService5、ProductSqlService1. 完整SQL分析可选部分&#xff08;条件筛选&#xff09;&#xff1a; 2. 涉及的表3. 总结4. 功能概述 查询指定管理员下所有产品所涉及的表&#xff1f;…

错误:pip报No module named ‘pip‘错怎么处理

有时候在执行pip更新失败后&#xff0c;再次执行pip命令时会提示ModuleNotFoundError: No module named pip’错误&#xff0c;导致pip命令无法使用 现象 重新打开一个cmd命令窗口&#xff0c;选择使用管理员权限打开&#xff1a;可以直接右键或是点击右侧功能&#xff0c;以…

SSM虾米音乐项目2--分页查询

1.分页查询的底层逻辑 首先根据用户输入的流派&#xff0c;进行模糊查询根据查询的数据进行分页需要前端用户提供pageNo(当前页数)和pageSize(每页的数据量)并且要从后端计算count(总数据量)和totalPage(总页数)&#xff0c;以及startNum(每页开始的记录)从而将对应的页面数据…

npm, yarn, pnpm之间的区别

前言 在现代化的开发中&#xff0c;一个人可能同时开发多个项目&#xff0c;安装的项目越来越多&#xff0c;所随之安装的依赖包也越来越臃肿&#xff0c;而且有时候所安装的速度也很慢&#xff0c;甚至会安装失败。 因此我们就需要去了解一下&#xff0c;我们的包管理器&#…

Idea Spring Initializr没有 Java 8选项解决办法

问题描述 在使用IDEA中的Spring Initializr创建新项目时&#xff0c;Java 版本近可选择Java17,21 。不能选择Java8;SpringBoot 版本也只有 3.x 问题原因 Spring 官方&#xff08; https://start.spring.io/&#xff09;不再提供旧版本的初始化配置 解决方案 方案 1 使用阿里…

使用 Vue 和 Canvas-Confetti 实现烟花动画特效

在开发中&#xff0c;为用户提供具有视觉冲击力的反馈是一种提升用户体验的好方法。今天&#xff0c;我们将结合 Vue 框架、canvas-confetti 和 Lottie 动画&#xff0c;创建一个动态对话框动画&#xff0c;其中包含炫酷的烟花特效。 效果图&#xff1a; 效果简介 当用户触发…