Manacher算法是一个求字符串的最长回文子串一种非常高效的方法,其时间复杂度为O(n)。下面分析以下其实行原理及代码:

1.首先对字符串进行预处理

因为回文分为奇回文和偶回文,分类处理比较麻烦,所以我们先要做一个预处理,在字符之间插入一个特殊字符(注意这个新插入的字符不能再原字符串中出现),这样无论原字符串是奇是偶,我们将它统一的转化为奇串。初此之外,我们还需在最开头也加一个特殊字符为了防止越界,例如:

原字符串 s =” abbaTNTabcba ” ,则经过转化后这个字符串会转变为:

sNew= “$#a#b#b#a#T#N#T#a#b#c#b#a#”

2.引入一个和处理过后长=长度相同的数组记录

引入一个数组假设计为p[snew.length()],其中 p[i] 表示以snew[i]为中心,半径为p[i]的最长回文子串,p[i]=1 则表示该回文子串就是senw[i]本身,下面是snew的最长回文子串半径:

由上图可知,snew[20]='c' 为中心的最长回文子串半径为6,由于第一个和最后一个字符都是#号,且也需要搜索回文,为了防止越界,由于字符串在结尾有’\0’,所以在字符串开头需要加上非#号字符(为了区分这里用的$)。通过p数组可以找到最大回文子串半径的最大值及其中心位置,就能确定最长回文子串了。所以现在问题转化为求p数组。

3.p数组(最长回文子串半径数组)的求法

Manacher算法利用开头提到的回文的左边是右边的镜像,让回文串起始的对比位置尽可能的大:

这里引入了两个新的变量id和mx,id为最大回文串中心的位置,mx为最大回文串的右边界,i为当前遍历带字符串的为位置。

这里分两种讨论:

一、mx > i

假设当前遍历到字符串的位置i,由于在遍历到id位置的时候已知最大回文子串,位置i还在上一个最大回文子串的范围内,所以可以利用其镜像认为,位置i以id为中心镜像到另一边的位置j是对等的。 在mx>i的条件下,又分为以下两种情况:

<1.mx - i > p[j] (图1)

此时,以j为中心的回文子串包含在以id为中心的回文子串内,由于i和j位置对等,所以以i为中心的回文子串包含在以id为中心的回文子串内,所以p[i] = p[j] = p[2 * id - i]。

<2. mx - i <= p[j] (图2)

此时,以j为中心的回文子串超过了以id为中心的回文子串边界,但是由于i和j位置对等,绿框部分还是相同的。所以其向右延伸的范围最大就是mx-i,剩下超过的部分谁也不能保证是否一致,只能通过循环对比判断,所以p[i] = mx - i。  

二、mx < i

此时镜像位置对预判位置起不到作用,只能从长度为1进行对比,所以此时p[i]=1; 

下面为实现代码:

#include<string>
#include <vector>
#include<iostream>
#include <algorithm> using namespace std; string Manacher(string s)
{
string snew="$#"; //预处理,只要在原字符串中不可能出现即可
int len=s.length();
for (int i=0;i<len;i++)
{
snew+=s[i];
snew+="#";
}
int len2=snew.length();
int lenans=-1; // 最长回文子串的长度
int pos=-1; // 最长回文子串中心点的位置
vector<int> p(len2, 0);
int id=0; //当前中心点的位置
int mx=0; //最大回文子串的右边界
for (int i=1;i<len2;i++)
{
if (i<mx)
p[i]=min(p[2*id-i],mx-i);
else
p[i]=1;
while(snew[i-p[i]]==snew[i+p[i]]) //最左边sNew[0]='$',最右边sNew[sNew.size()] = '\0',无需判断边界
p[i]++;
if(p[i]+i>mx)
//我们每走一步i,都要和mx比较,我们希望mx尽可能的远,这样才能更有机会执行if(i<mx)这句代码,从而提高效率
{
id=i;
mx=i+p[i];
}
if (p[i]-1>lenans)
{
lenans=p[i]-1;
pos=i;
}
}
string::iterator iStart=s.begin()+(pos-lenans-1)/2;
//将最长回文子串起始位置转换回原串
return string(iStart,iStart+lenans);
//也可以return lenans,最长回文子串的长度
} int main()
{
string s;
cin >> s;
cout << s << " 的最长回文子串为: " << Manacher(s) << endl;
return 0;
}

Manacher(马拉车)算法的更多相关文章

  1. Manacher(马拉车)算法(jekyll迁移)

    layout: post title: Manacher(马拉车)算法 date: 2019-09-07 author: xiepl1997 cover: 'assets/img/manacher.p ...

  2. manacher(马拉车算法)

    Manacher(马拉车算法) 序言 mannacher 是一种在 O(n)时间内求出最长回文串的算法 我们用暴力求解最长回文串长度的时间复杂度为O(n3) 很明显,这个时间复杂度我们接受不了,这时候 ...

  3. HDU - 3068 最长回文manacher马拉车算法

    # a # b # b # a # 当我们遇到回判断最长回文字符串问题的时候,若果用暴力的方法来做,就是在字符串中间添加 #,然后遍历每一个字符,找到最长的回文字符串.那么马拉车算法就是在这个基础上进 ...

  4. Manacher (马拉车) 算法:解决最长回文子串的利器

    最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...

  5. manacher马拉车算法

    Manacher算法讲解 总有人喜欢搞事情,出字符串的题,直接卡掉了我的40分 I.适用范围 manacher算法解决的是字符串最长回文子串长度的问题. 关键词:最长 回文 子串 II.算法 1.纯暴 ...

  6. 最长回文子串 —— Manacher (马拉车) 算法

    最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...

  7. [模板] Manacher(马拉车)算法

    用途 求回文子串 做法 先考虑回文子串以某字符为中心的情况,即长度为奇数 推着做,记rad[i]为以i位置为中心的最大半径(包含中点) 考虑怎么求rad[i].找之前的一个右端点最靠右的位置p,设它的 ...

  8. Manacher(马拉车)算法详解

    给定一个字符串,求出其最长回文子串 eg:  abcba 第一步: 在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里). 如  原来ma  /*  a    b a    b   c ...

  9. Manacher's Algorithm 马拉车算法

    这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...

随机推荐

  1. Javascript自增、自减运算符

    JavaScript自增.自减运算符与表达式语法 var i++; var-- 声明变量 i-- 变量名 ++ -- 自增运算符 JavaScript自增.自减运算符与表达式 JavaScript自增 ...

  2. redis-start

    start CAP BASE: 基本可用 Basically Available 软状态 Soft state 最终一致 Eventually consistent Redis:REmote DIct ...

  3. AJAX技术初级探索

    一 概念 Ajax技术能够实现页面以异步方式从服务器获取信息,无需刷新页面 Ajax的核心技术是XMLHttpRequest对象(XHR) 异步实际就是请求数据的代码不会阻塞页面向下执行 二 原生Aj ...

  4. Mybatis 元素类型为 "resultMap" 的内容必须匹配 "(constructor?,id*,result*,association*,collection*,discriminat

    <resultMap id="BaseResultMap" type="com.youotech.tl_cons_credit_rating.entity.TL_C ...

  5. rg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TL_C_CONS_ExtendController':

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TL_ ...

  6. EditPlus 注册码在线生成

    虽然editplus现在不常用,但是它轻便,我还是很喜欢的,推荐一个注册码生成器,真是好好用 http://www.jb51.net/tools/editplus/ 点击链接输入自己想要的用户名,就能 ...

  7. js遍历传参到html

    <p id="subp" hidden><button id= "upsub"shiro:hasPermission="sys:me ...

  8. 基于Ubuntu和基于Debian的Linux Mint 20新特性一览

    导读 Linux Mint 20 将基于 Ubuntu 20.04 LTS,同时,其 LMDE(Linux Mint Debian Edition,Debian 版本)4 也将到来,LMDE 版本基于 ...

  9. c# Linq List<T>去除其中重复的项

    //要去重的 list<T> listTest: var resultlist= listTest.GroupBy(p =>p.Id).Select(g => g.First( ...

  10. 计算机二级-C语言-程序修改题-190114记录-对整型变量进行取余操作可以取得各个位上的值。

    //给定程序中fun函数的功能是:从低位开始取出长整形变量s中奇数位上的数,依次构成一个新的数放在t中.高位仍在高位,低位仍在低位.例如:当s中的数为7654321时,t中的数为7531. //重难点 ...