[转]回文串判断算法——Manacher算法

2023-11-06

以下文字转自 ddyyxx博客:
Manacher算法总结

Manacher算法总结


算法总结第三弹 manacher算法,前面讲了两个字符串相算法——kmp和拓展kmp,这次来还是来总结一个字符串算法,manacher算法,我习惯叫他 “马拉车”算法。
相对于前面介绍的两个算法,Manacher算法的应用范围要狭窄得多,但是它的思想和拓展kmp算法有很多共通支出,所以在这里介绍一下。Manacher算法是查找一个字符串的最长回文子串的线性算法。

在介绍算法之前,首先介绍一下什么是回文串,所谓回文串,简单来说就是正着读和反着读都是一样的字符串,比如abba,noon等等,一个字符串的最长回文子串即为这个字符串的子串中,是回文串的最长的那个。

  • 一种是回文串长度是奇数的情况,
  • 另一种是回文串长度是偶数的情况,枚举中点再判断是否是回文串,这样能把算法的时间复杂度降为O(n^2)

但是当n比较大的时候仍然无法令人满意,Manacher算法可以在线性时间复杂度内求出一个字符串的最长回文字串,达到了理论上的下界。

1.Manacher算法原理与实现

下面介绍Manacher算法的原理与步骤。

首先,Manacher算法提供了一种巧妙地办法,将长度为奇数的回文串和长度为偶数的回文串一起考虑,具体做法是,在原字符串的每个相邻两个字符中间插入一个分隔符,同时在首尾也要添加一个分隔符,分隔符的要求是不在原串中出现,一般情况下可以用#号。下面举一个例子:
Alt text

(1)Len数组简介与性质

Manacher算法用一个辅助数组Len[i]表示以字符T[i]为中心的最长回文半径字串的最右字符到T[i]的长度,比如以T[i]为中心的最长回文字串是T[l,r],那么Len[i]=r-i+1。
对于上面的例子,可以得出Len[i]数组为:
Alt text
Len数组有一个性质,那就是Len[i]-1就是该回文子串在原字符串S中的长度

证明:
1、显然L=2Len[i]1 即为新串中以Str[i]为中心最长回文串长度。
2、以Str[i]为中心的回文串一定是以#开头和结尾的,例如“#b#b#”或“#b#a#b#”所以L 减去最前或者最后的‘#’字符就是原串中长度 的二倍,即原串长度为(L-1)/2,化简的Len[i]-1。得证。 依次从前往后求Len 数组就可以了,这里用到了DP(动态规划)的思想, 也就是求P[i] 的时候,前面的Len[]值已经得到了,我们利用回文串的特殊性质可以进行一个大大的优化。

(2)Len数组的计算

首先从左往右依次计算Len[i],当计算Len[i]时,Lenj已经计算完毕。设P为之前计算中最长回文子串的右端点的最大值,并且设取得这个最大值的位置为po,分两种情况:

  • 第一种情况:i<=P

那么找到i相对于po的对称位置,设为j,那么如果Len[j]<Pi,如下图:

这里写图片描述

那么说明以j为中心的回文串一定在以po为中心的回文串的内部,且j和i关于位置po对称,由回文串的定义可知,一个回文串反过来还是一个回文串,所以以i为中心的回文串的长度至少和以j为中心的回文串一样,即Len[i]>=Len[j]。因为Len[j]<Pi,所以说i+Len[j]<P。由对称性可知Len[i]=Len[j]

如果Len[j]>=Pi,由对称性,说明以i为中心的回文串可能会延伸到P之外,而大于P的部分我们还没有进行匹配,所以要从P+1位置开始一个一个进行匹配,直到发生失配,从而更新P和对应的po以及Len[i]。

这里写图片描述

  • 第二种情况: i>P

如果i比P还要大,说明对于中点为i的回文串还一点都没有匹配,这个时候,就只能老老实实地一个一个匹配了,匹配完成后要更新P的位置和对应的po以及Len[i]。

这里写图片描述

2.时间复杂度分析

Manacher算法的时间复杂度分析和Z算法类似,因为算法只有遇到还没有匹配的位置时才进行匹配,已经匹配过的位置不再进行匹配,所以对于T字符串中的每一个位置,只进行一次匹配,所以Manacher算法的总体时间复杂度为O(n),其中n为T字符串的长度,由于T的长度事实上是S的两倍,所以时间复杂度依然是线性的。
下面是算法的实现,注意,为了避免更新P的时候导致越界,我们在字符串T的前增加一个特殊字符,比如说‘$’,所以算法中字符串是从1开始的。

const int maxn=1000010;  
char str[maxn];//原字符串  
char tmp[maxn<<1];//转换后的字符串  
int Len[maxn<<1];  
//转换原始串  
int INIT(char *st)  
{  
    int i,len=strlen(st);  
    tmp[0]='@';//字符串开头增加一个特殊字符,防止越界  
    for(i=1;i<=2*len;i+=2)  
    {  
        tmp[i]='#';  
        tmp[i+1]=st[i/2];  
    }  
    tmp[2*len+1]='#';  
    tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界  
    tmp[2*len+3]=0;  
    return 2*len+1;//返回转换字符串的长度  
}  
//Manacher算法计算过程  
int MANACHER(char *st,int len)  
{  
     int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值  
     for(int i=1;i<=len;i++)  
     {  
         if(mx>i)  
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小  
         else  
         Len[i]=1;//如果i>=mx,要从头开始匹配  
         while(st[i-Len[i]]==st[i+Len[i]])  
         Len[i]++;  
         if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值  
         {  
             mx=Len[i]+i;  
             po=i;  
         }  
         ans=max(ans,Len[i]);  
     }  
     return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度   
  }  

转载于:https://www.cnblogs.com/voidsky/articles/5373918.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[转]回文串判断算法——Manacher算法 的相关文章

  • 五大常用经典算法

    五大常用算法之一 分治算法 一 基本概念 在计算机科学中 分治法是一种很重要的算法 字面上的解释是 分而治之 就是把一个复杂的问题分成两个或更多的相同或相似的子问题 再把子问题分成更小的子问题 直到最后子问题可以简单的直接求解 原问题的解即
  • 01背包问题变种:从长度为n的数组里选出m个数使和为固定值sum

    这个问题是我从leetcode上一道问题所想到的 原题 如果是从数组中选出2个数相加使之成为固定的数sum 这当然很简单 把数组中的数字遍历一遍 判断另一个数字是否也在数组中即可 代码如下 vector
  • 《Linux From Scratch》第三部分:构建LFS系统 第六章:安装基本的系统软件- 6.29. Coreutils-8.23...

    Coreutils 软件包包含用于显示和设置基本系统特性的工具 大概编译时间 2 5 SBU 需要磁盘空间 193 MB 6 29 1 安装 Coreutils POSIX 要求 Coreutils 中的程序即使在多字节语言环境也能正确识别
  • python 历险记(五)— python 中的模块

    目录 前言 基础 模块化程序设计 模块化有哪些好处 什么是 python 中的模块 引入模块有几种方式 模块的查找顺序 模块中包含执行语句的情况 用 dir 函数来窥探模块 python 的内置模块有哪些 结语 参考文档 系列文章列表 前言
  • 白盒测试相关的一些知识

    在白盒测试中 可以使用各种测试方法进行测试 下面这篇文章 可能比较枯燥 如果不乐意读 可以先收藏 如果在你的工作中真遇到白盒测试的话 可以回过头再来看看 还是值得读一读 一般来说 白盒测试时要考虑以下5个问题 1 测试中尽量先用自动化工具来
  • 4399 C++笔试题

    1 写出一个函数 取到链表中倒数第二个节点 双链表 node getSec List mylist return mylist m tail gt m prev m prev为链表前指针 单链表 node getSec List mylis
  • JavaScript实现数据结构 -- 链表

    文章目录 链表 链表的特点 链表和数组的区别 JS模拟链表 遍历链表 插入节点 删除节点 链表应用 删除链表中的节点 leetcode 237 思路 代码 反转链表 leetcode 206 思路 代码 链表 链表和数组一样是有多个元素组成
  • 逆波兰表达式求值(C语言实现)

    实验项目 从文本文件输入任意一个语法正确的 中缀 表达式 显示并保存该表达式 利用栈结构 把上述 中缀 表达式转换成后缀表达式 并显示栈的状态变化过程和所得到的后缀表达式 利用栈结构 对上述后缀表达式进行求值 并显示栈的状态变化过程和最终结
  • Hash映射理解

    先说数组 数组优点之一 能通过索引很快定位到值 hashmap 就是利用了数组这个优点 对比 线性映射 定义一个数组 数组的元素是结构体 结构体包括 一对键 值 伪代码表示 a 0 struct Bill 5 a 1 struct KK 6
  • 亚利桑那州立大学周纵苇:研习 U-Net ——现有的分割网络创新

    雷锋网AI研习社按 经典的 Encoder Decoder 结构在目标分割问题中展现出了举足轻重的作用 然而这样一个相对固定的框架使得模型在感受野大小和边界分割精度两方面很难达到兼顾 本次公开课 讲者以 U Net 为案例分析 总结现有的分
  • 数据结构小白之插入排序算法

    1 插入排序 1 1 思路 将n个需要排序的元素看成两个部分 一个是有序部分 一个是无序部分 开始的时候有序表只有一个元素 无序表有n 1个元素 排序过程中每次从无序表中取出元素 然后插入到有序表的适当位置 从而成为新的有序表 类似排队 如
  • 数据结构之图的两种遍历实现(C语言版)

    上一期文章分享完了图的两种遍历方式 也是两种很重要的算法 DFS和BFS 这两种算法的应用和重要性我就不多说了 内行的人懂的都懂 今天这文章重要就是来上机实现这两种算法 又由于这两种算法都可以由邻接矩阵和邻接表来表示 博主分享的代码都是上机
  • 数据结构与算法书籍推荐

    学习数据结构与算法 还是很有必要看几本相关的书籍 但根据不同基础的人 合适看的书也不一样 因此 针对不同层次 不同语言的人 推荐几本市面上口碑不错的书 1 入门级 针对刚入门的同学 建议不要急着去看那些经典书 像 算法导论 算法 这些比较经
  • 数据结构——计算节点个数和二叉树高度(C语言版)

    摘自 数据结构 计算节点个数和二叉树高度 C语言版 作者 正弦定理 发布时间 2020 12 12 23 27 09 网址 https blog csdn net chinesekobe article details 111086664
  • CRC校验(二)

    CRC校验 二 参考 https blog csdn net liyuanbhu article details 7882789 https www cnblogs com esestt archive 2007 08 09 848856
  • Linux 内核中的 Device Mapper 机制

    Linux 内核中的 Device Mapper 机制 尹 洋 在读博士生 尹洋 中科院计算所国家高性能计算机工程技术研究中心的在读博士生 主要从事服务部署和存储资源管理以及Linux块设备一级的开发和研究工作 简介 本文结合具体代码对 L
  • 区块链中的哈希算法

    区块链中的密码学 密码学在区块链中的应用主要有两个 哈希算法与非对称加密算法 这次主要对哈希算法进行详细的说明 哈希算法 哈希算法的特点有 1 输入可以为任意大小的字符串 2 产生固定大小的输出 3 可以在合理的时间内算出输出值 若要满足密
  • 查找数组中第二大的数

    快速找出一个数组中的最大数 第二大数 思路 如果当 前元素大于最大数 max 则让第二大数等于原来的最大数 max 再把当前元素的值赋给 max 如果当前的元素大于等于第二大数secondMax的值而小于最大数max的值 则要把当前元素的值
  • 基数排序代码实现

    详情请看排序总结 传送门 https blog csdn net m0 52711790 article details 121914543 基数排序的知识点我就不贴出来 相信都能搜到对应概念解释 下面就直接上代码 代码解释其实也很清晰了
  • 浅谈归并排序:合并 K 个升序链表的归并解法

    在面试中遇到了这道题 如何实现多个升序链表的合并 这是 LeetCode 上的一道原题 题目具体如下 用归并实现合并 K 个升序链表 LeetCode 23 合并K个升序链表 给你一个链表数组 每个链表都已经按升序排列 请你将所有链表合并到

随机推荐

  • 解决Windows 组件存储已损坏,0x80073712错误

    在 Windows 8 与 Windows Server 2012 当系统组件有损毁时 我们可以在不影响目前系统状况下来检查与修复系统组件 如下 当我添加功能组件时报如下错误 明显可以看出我的组件存储已损坏 那今天就让我告诉大家解决方案 我
  • 算法、数据结构可视化

    算法 数据结构可视化 一 总结 一句话总结 比如算法 数据结构 很多都有可视化 学习要知道用可视化更好的学习 1 可视化数据结构 http www cs usfca edu galles visualization Algorithms h
  • ffmpeg的使用

    目录 ffmpeg的下载 配置 下载 版本说明 环境变量配置 ffmpeg处理m3u8 ts的常用命令 ffmpeg是一个十分强大的音视频处理工具 提供转码 播放等基础功能 功能十分全面 强大 但命令繁多复杂 通常不直接使用 而是集成在带G
  • vue门户网站,滚动到可视化区域展示动画效果方案

    1 准备两个工具库 1 1 animate css 动画库 动画效果展示 Animate css A cross browser library of CSS animations 1 2 wowjs 负责滚动到可视化区域 展示animat
  • 各种正交以及正交和

    20200924 笛卡尔积里面选取交集为空或者交集等于恒值 自己定义其他条件 的 相乘之和 https www 59baike com a 365039 35 i 正交和 编程中 经常出现正交这个词 正交指相互独立 不可替代 并且组合起来可
  • openwrt上nginx扩展模块的支持

    在固件开发过程中 上层业务层需要用到nginx的一些扩展模块 比如ngx devel kit master set misc nginx module master nginx push stream module master ngx c
  • 幸运数的划分

    题目描述 判断一个正整数n是否能被一个 幸运数 整除 幸运数是指一个只包含4或7的正整数 如 7 47 477等都是幸运数 17 42则不是幸运数 输入 一行一个正整数n 1 n 1000 输出 一行一个字符串 如果能被幸运数整除输出 YE
  • ArgumentError: Python argument types in ****** did not match C++ signature:

    这种错误一般是由于数据类型错误 我是这样的 ArgumentError Traceback most recent call last tmp ipykernel 2665 3113553009 py in 26 27 filename c
  • 三极管的三种状态

    三极管的三种状态也叫三个工作区域 即 截止区 放大区和饱和区 1 截止区 三极管工作在截至状态 当发射结电压Ube小于0 6 0 7V的导通电压 发射结没有导通 集电结处于方向偏执 没有放大作用 2 放大区 三极管的发射极加正向电压 集电极
  • 斗图网斗图全站爬取(用正则表达式re)

    import re import requests import os class doutu spyder first url first name headers User Agent Mozilla 5 0 Windows NT 10
  • python安装pywin32出现DLL load failed的解决办法

    在安装pywin32最后一步时会提示DLL load failed 安装完成后import win32com client也会出现同样的问题 解决办法 把 Lib site packages pywin32 system32下的三个dll文
  • 如何用计算机发匿名短信,电脑如何给手机发信息_电脑匿名给手机发短信

    2017 01 09 08 24 27 腾讯应用宝 手机和电脑都安装好应用宝后 手机打开USB调试模式 电脑运行应用宝 无线连接手机后 就可以在电脑上发手机信息了 不过信息费扣的是你手机卡上的 无非就是借用电脑的键盘打字 2017 01 1
  • anaconda中如何安装tflearn?

    1 打开anaconda prompt 2 activate tensorflow 环境名 3 pip install tflearn 4 如何安装tflearn
  • element-ui二次封装(下拉菜单el-dropdown)

    样式效果 效果1 效果2 效果1组件调用
  • 如何查看中科院2020期刊分区表?

    如何查看中科院2020期刊分区表 2020年中国科学院文献情报中心期刊分区表 以下简称基础版分区表 正式发布 2020年基础版分区表在秉承方法科学和数据客观的基础上 延续使用2019年基础版分区表的方法体系 2020基础版分区表继续突出支持
  • vue怎么引入环境变量_vue 文件中如何获取环境变量

    环境变量的传递路径 命令行命令 gt Webpack gt Webpack 加载的各种 js 和 Vue 文件 你的 nodejs 运行命令启动项目 package json dev cross env BABEL ENV developm
  • Python 日期时间datetime 加一天,减一天,加减一小时一分钟

    当前日期时间 import datetime print datetime datetime now 2018 05 08 16 53 30 101000 1 2 3 格式化时间 import datetime print datetime
  • FPGA零基础学习之Vivado-数码管驱动设计实验

    FPGA零基础学习之Vivado 数码管驱动设计实验 本系列将带来FPGA的系统性学习 从最基本的数字电路基础开始 最详细操作步骤 最直白的言语描述 手把手的 傻瓜式 讲解 让电子 信息 通信类专业学生 初入职场小白及打算进阶提升的职业开发
  • 【目标检测】

    test coco py cal area import os import pickle import cv2 import matplotlib pyplot as plt import numpy as np from pathlib
  • [转]回文串判断算法——Manacher算法

    以下文字转自 ddyyxx博客 Manacher算法总结 Manacher算法总结 算法总结第三弹 manacher算法 前面讲了两个字符串相算法 kmp和拓展kmp 这次来还是来总结一个字符串算法 manacher算法 我习惯叫他 马拉车