马拉车算法(求最长回文串)

马拉车算法(求最长回文串)参考博客 https blog csdn net article details https blog csdn net u0 article details https blog csdn net article details utm source apphtt

参考博客:

  • https://blog.csdn.net/_/article/details/
  • https://blog.csdn.net/u0/article/details/
  • https://blog.csdn.net/_/article/details/?utm_source=app
  • https://www.cnblogs.com/grandyang/p/4475985.html

马拉车算法:

  • 什么是马拉车?
    Manacher算法,又叫“马拉车”,它可以在时间复杂度和空间复杂度都是O(n)的情况下,求出一个字符串的最长回文串长度(一般用法,也可以求字符串中回文串的个数)

  • 第一步,重组字符串
    例如: noon
    重组后:$#n#o#o#n#
    那为什么要这样做呢?
    1.首先,这样做的好处是不论原字符串是奇数还是偶数个,处理之后得到的字符串的个数都是奇数个,这样就不用分情况讨论了,而可以一起搞定。
    2.接下来我们还需要和处理后的字符串t等长的数组p,其中 p[i] 表示以 t[i] 字符为中心的回文子串的半径,若 p[i] = 1,则该回文子串就是 t[i] 本身,那么我们来看一个简单的例子:










    # 1 # 2 # 2 # 1 # 2 # 2 # 1 2 1 2 5 2 1 6 1 2 3 2 1 
  • 第二步,理解下面这段代码
    // id 代表中心位置, mx 代表最右边的位置 p[i] = mx > i ? min(p[id * 2 - i],mx - i):1; 
  • 马拉车完整代码(求最长回文子串的长度):
/* 马拉车模板题: https://www.luogu.org/problemnew/solution/P3805 */ //求最长回文串的长度 #include  
     using namespace std; const int MAXN = ; int hw[MAXN]; //马拉车 inline int Manacher(string s) { 
    //转换字符串 memset(hw, 0, sizeof(hw)); int len = s.length(); string nowString = "$#"; for(int i = 0; i < len; i++){ 
    nowString += s[i]; nowString += "#"; } //防止越界访问 len = nowString.length(); int maxRight = 0, mid = 0, maxAns = 0; //maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径 for(int i = 1; i < len; i++){ 
    if(maxRight > i){ 
    //当中心点没超过最右边maxRight hw[i] = min(maxRight - i, hw[(mid<<1) - i]); } else{ 
    //否则,就重新往外扩 hw[i] = 1; } //如果当前不是最长, 就往外扩 while(nowString[i + hw[i]] == nowString[i - hw[i]]){ 
    ++hw[i]; } //更新最右边的位置, 同时更新最长字串的半径 if(i + hw[i] > maxRight){ 
    maxRight = i + hw[i]; mid = i; } maxAns = max(hw[i], maxAns); } //最长字串的长度等于半径减1 return (maxAns - 1); } int main() { 
    ios::sync_with_stdio(false); string s; while(cin >> s){ 
    cout << Manacher(s) << endl; } return 0; } 
  • 马拉车求最长回文串的个数
//求回文串的个数 inline int Manacher(string s) { 
    //转换字符串 memset(hw, 0, sizeof(hw)); int len = s.length(); string nowString = "$#"; for(int i = 0; i < len; i++){ 
    nowString += s[i]; nowString += "#"; } //防止越界访问 nowString += "^"; len = nowString.length(); int maxRight = 0, mid = 0, maxAns = 0, numAns = 0; //maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径 for(int i = 1; i < len; i++){ 
    if(maxRight > i){ 
    //当中心点没超过最右边maxRight hw[i] = min(maxRight - i, hw[(mid<<1) - i]); } else{ 
    //否则,就重新往外扩 hw[i] = 1; } //如果当前不是最长, 就往外扩 while(nowString[i + hw[i]] == nowString[i - hw[i]]){ 
    ++hw[i]; } //更新最右边的位置, 同时更新最长字串的半径 if(i + hw[i] > maxRight){ 
    maxRight = i + hw[i]; mid = i; } //求最长回文串的长度 //maxAns = max(hw[i], maxAns); //求回文串的个数 numAns += (hw[i] / 2); } //最长字串的长度等于半径减1 //return (maxAns - 1); //回文串的个数 return numAns; } 

这里我们来探讨下为什么是:

//求回文串的个数 numAns += (hw[i] / 2); 
  • 求最长回文串
//求最长回文串 inline string Manacher(string s) { 
    //转换字符串 memset(hw, 0, sizeof(hw)); int len = s.length(); string nowString = "$#"; for(int i = 0; i < len; i++){ 
    nowString += s[i]; nowString += "#"; } //防止越界访问 nowString += "^"; len = nowString.length(); int maxRight = 0, mid = 0, maxLen = 0, maxPoint = 0; //maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径 for(int i = 1; i < len; i++){ 
    if(maxRight > i){ 
    //当中心点没超过最右边maxRight hw[i] = min(maxRight - i, hw[(mid<<1) - i]); } else{ 
    //否则,就重新往外扩 hw[i] = 1; } //如果当前不是最长, 就往外扩 while(nowString[i + hw[i]] == nowString[i - hw[i]]){ 
    ++hw[i]; } //更新最右边的位置, 同时更新最长字串的半径 if(i + hw[i] > maxRight){ 
    maxRight = i + hw[i]; mid = i; } if(hw[i] > maxLen){ 
    maxLen = hw[i]; maxPoint = i; //最长回文串的中心位置 } } //截取最长回文串 //这里为啥这样写,在本博客前文已经提到过: 1. 第一步,重组字符串 return s.substr((maxPoint - maxLen) / 2, maxLen - 1); } 

后续再补上一些例题,以及求字符串中所有的回文串个数,最长的回文串!

2019.07.26, 已经将基本的马拉车算法应用补上.

待补题: 杭电6599

http://acm.hdu.edu.cn/showproblem.php?pid=6599

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/229117.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午5:26
下一篇 2026年3月16日 下午5:26


相关推荐

  • 使用 Binlog 和 Canal 从 MySQL 抽取数据

    使用 Binlog 和 Canal 从 MySQL 抽取数据数据抽取是 ETL 流程的第一步 我们常会需要从多个不同的 MySQL 实例中抽取数据 存入一个中心节点 或直接进入 Hive 借助 Canal 项目 我们能够通过 MySQLBinlog 进行数据抽取

    2026年3月18日
    12
  • QThread与QWidget使用[通俗易懂]

    QThread与QWidget使用[通俗易懂] 原文链接:http://hi.baidu.com/cyclone/blog/item/65f3f603294f2e783812bb51.html注意:请优先考虑Qt线程基础(QThread、QtConcurrent等)dbzhang8002011.06.18 本文主要内容: 在任务一中,用四种方式实现:点击界面按钮,开线程运行一段程序,结果显示在一个La…

    2022年5月28日
    55
  • 机器学习之文本分类(附带训练集+数据集+所有代码)

    机器学习之文本分类(附带训练集+数据集+所有代码)我本次对4类文本进行分类((所有截图代码和数据集最后附带免费下载地址))主要步骤:1.各种读文件,写文件2.使用jieba分词将中文文本切割3.对处理之后的文本开始用TF-IDF算法进行单词权值的计算4.去掉停用词5.贝叶斯预测种类文本预处理:除去噪声,如:格式转换,去掉符号,整体规范化遍历的读取一个文件下的每个文本中文分词…

    2022年6月2日
    25
  • 18.Linux软件安装之Rpm安装

    18.Linux软件安装之Rpm安装RedHatPackag 的缩写 是红帽软件包工具 RPM 的提供可升级 具有强大查询功能 支持安全验证的通用型 Linux 软件包管理工具 由于这种软件管理方式非常方便 所以逐渐被其他 Linux 发行版所借用 现在已经称为 Linux 平台下通用的软件包管理方式

    2026年3月20日
    2
  • c语言gdi绘图_程序设计的基本步骤是

    c语言gdi绘图_程序设计的基本步骤是本文将实现对基本图形的绘制:windows程序画图,大体上有3种方法:(1)你告诉系统点的坐标和颜色,系统通过SetPixel来画。类似的,通过GetPixel来获取某一点像素值。(2)使用MoveToEx、LineTo来划线,MoveToEx设置起点坐标,LineTo设置终点坐标,或者使用Polyline函数,这个函数接受一个POINT类型的数组,通过数组里的点连线。(3)windows…

    2022年8月18日
    8
  • forEach跳出本次循环继续下一次循环

    forEach跳出本次循环继续下一次循环不能用 continue 用 returnlocati value lettest lettemp value forEach element gt 如果是直辖市 则市名会等于上一个省名 此时不把市名加入 test 内 则选择北京市 朝阳区的情况时 不会出现 北京市北京市朝阳区 的字样 if element name temp return els

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号