Manacher

一、背景

1975年,Manacher发明了Manacher算法(中文名:马拉车算法),是一个可以在O(n)的复杂度中返回字符串s中最长回文子串长度的算法,十分巧妙。

让我们举个栗子,栗子:

1.字符串:abbababa        最长回文子串:5(abbababa

2.字符串:abcbbabbc      最长回文子串:7(abcbbabbc

3.字符串:abccbaba        最长回文子串:6(abccbaba)

传统方法是,遍历每个字符,以该字符为中心向两边查找。时间复杂度为O(n^2),效率很差;

而这个神奇的Manacher算法将复杂度提升到了O(n)。

来一起瞅一瞅它是如何工作的吧。

二、算法过程分析

回文分为奇回文(ababa)和偶回文(abba),这里比较难以处理,我们使用一个(sao)(cao)(zuo)(很重要)。我们将字符串首尾和每个字符间插入一个字符(注意:这个自符在串中并未出现)例如:'#'.

栗子!栗子!s='abbadcacda'先转化成s_new='$#a#b#b#a#d#c#a#c#d#a#\0'('$'与'\0',是边界,下面的代码中可以看到)

这样原串中的偶回文(abba)与奇回文(adcacda),变成了(#a#d#d#a#)与(#a#d#c#a#c#d#a#)两个奇回文

定义数组p[],用p[i]表示以i为中心的最长回文半径。栗子在这里:

那,p[i]该如何求呢?很显然,p[i]-1正好就是原字符中的最长回文串长度了。

让我们一起找到正解。

请看下图:

定义两个变量mx和id。mx就是以id为中心的最长回文右边界,也就是mx=id+p[id],随后我们需要mx做出它的最大贡献。

假设我们在求p[i](以i为中心的最长回文半径),如果i<mx(如上图),那么我们就用mx和j来更新到我们已知的可以更新的最大长度,代码如下:

if(i<mx)
p[i]=min(p[2*id-i],mx-i);

2*id-i是i关于id的对称点(上图j)(证明:i-id=id-j),而p[j]表示以j为中心的最长回文半径,这样我们就可以利用p[j]和mx加快速度了。

为什么要用p[j]和mx-i取min来更新,什么鬼?

淡定,淡定。我们想一下,p[j](以j为中心的最长回文半径)是已经知道了(因为是从前面扫过来的),若是p[j]>mx-i,我们是可以知道以j为中心,以mx的对称点到j的距离为半径形成的回文字符串是肯定存在的,并且id的左边直到mx的对称点与id的右边 直到mx是一一对应的,不难理解mx是i目前可以更新到的最大回文半径;若p[j]<mx-i,证明j的回文半径不到mx的对称点到j的距离,再次通过(id的左边直到mx的对称点与id的右边 直到mx是一一对应的),不难想到p[i]=p[j]。

取完min就是最大的回文半径吗?

显然不是,接下来的暴力往后扫就好了(学oi的都有暴力倾向)。

三、代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; char s[11000002];
char s_new[21000002];//存添加字符后的字符串
int p[21000002]; int Init() {//形成新的字符串
int len=strlen(s);//len是输入字符串的长度
s_new[0]='$';//处理边界,防止越界
s_new[1]='#';
int j=2;
for(int i=0;i<len;i++) {
s_new[j++]=s[i];
s_new[j++]='#';
}
s_new[j]='\0';//处理边界,防止越界(容易忘记)
return j;// 返回s_new的长度
} int Manacher() {//返回最长回文串
int len=Init();//取得新字符串的长度, 完成向s_new的转换
int max_len=-1;//最长回文长度
int id;
int mx=0;
for(int i=1;i<=len;i++) {
if(i<mx)
p[i]=min(p[2*id-i],mx-i);//上面图片就是这里的讲解
else p[i]=1;
while(s_new[i-p[i]]==s_new[i+p[i]])//不需边界判断,因为左有'$',右有'\0'标记;
p[i]++;//mx对此回文中点的贡献已经结束,现在是正常寻找扩大半径
if(mx<i+p[i]) {//每走移动一个回文中点,都要和mx比较,使mx是最大,提高p[i]=min(p[2*id-i],mx-i)效率
id=i;//更新id
mx=i+p[i];//更新mx
}
max_len=max(max_len,p[i]-1);
}
return max_len;
} int main()
{
scanf("%s",&s);
printf("%d",Manacher());
return 0;
}

四、复杂度

完结撒花(复杂度不会证明呀,因为我是蒟蒻)

Manacher(马拉车)————O(n)回文子串的更多相关文章

  1. Manacher算法——最长回文子串

    一.相关介绍 最长回文子串 s="abcd", 最长回文长度为 1,即a或b或c或d s="ababa", 最长回文长度为 5,即ababa s="a ...

  2. Manacher算法----最长回文子串

    题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些 ...

  3. 马拉车算法——求回文子串个数zoj4110

    zoj的测评姬好能卡时间.. 求回文子串的个数:只要把p[i]/2就行了: 如果s_new[i]是‘#’,算的是没有中心的偶回文串 反之是奇回文串 /* 给定两个字符串s,t 结论:s,t不相同的第一 ...

  4. Manacher 求最长回文子串算法

    Manacher算法,是由一个叫Manacher的人在1975年发明的,可以在$O(n)$的时间复杂度里求出一个字符串中的最长回文子串. 例如这两个回文串“level”.“noon”,Manacher ...

  5. manacher求最长回文子串算法

    原文:http://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个 ...

  6. hdu 3068 最长回文(manacher&amp;最长回文子串)

    最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  7. manacher hihoCoder1032 最长回文子串

    居然能够做到O(n)的复杂度求最长回文.,也是给跪了. 以下这个人把manacher讲的很好,,能够看看 http://blog.csdn.net/xingyeyongheng/article/det ...

  8. hdu 3068 最长回文 【Manacher求最长回文子串,模板题】

    欢迎关注__Xiong的博客: http://blog.csdn.net/acmore_xiong?viewmode=list 最长回文                                 ...

  9. manacher求最长回文子串算法模板

    #include <iostream> #include <cstring> #include <cstdlib> #include <stdio.h> ...

  10. 【LeetCode】5. Longest Palindromic Substring 最大回文子串

    题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

随机推荐

  1. MySQL redo与undo日志解析

    前言: 前面文章讲述了 MySQL 系统中常见的几种日志,其实还有事务相关日志 redo log 和 undo log 没有介绍.相对于其他几种日志而言, redo log 和 undo log 是更 ...

  2. BUAA软件工程_软件案例分析

    写在前面 项目 内容 所属课程 2020春季计算机学院软件工程(罗杰 任健) (北航) 作业要求 软件案例分析 课程目标 培养软件开发能力 本作业对实现目标的具体作用 对案例进行分析以学习软件开发的经 ...

  3. MzzTxx——博客目录

    准备阶段 团队介绍 需求分析 技术规格说明书 功能规格说明书 Alpha 阶段任务分配 团队贡献分分配方案 Scrum Meeting Alpha 2021.04.21 Scrum Meeting 0 ...

  4. Spring context的refresh函数执行过程分析

    今天看了一下Spring Boot的run函数运行过程,发现它调用了Context中的refresh函数.所以先分析一下Spring context的refresh过程,然后再分析Spring boo ...

  5. [笔记] 《c++ primer》书店程序 Chapter2

    Sales_data.h 1 #ifndef SALES_DATA_H 2 #define SALES_DATA_H 3 4 #include "Version_test.h" 5 ...

  6. QTableWidget - 基础讲解(1)

    转载:http://www.cnblogs.com/fuqia/p/8904196.html QTableWidget是QT程序中常用的显示数据表格的空间,很类似于VC.C#中的DataGrid.说到 ...

  7. Ubuntu20 修改网卡名称

    Ubuntu 修改网卡名称 Ubuntu修改网卡名称 Ubuntu 版本: Ubuntu 20.04 查看当前网卡名称 root@it:~# ip add 1: lo: <LOOPBACK,UP ...

  8. MySQL报错ERROR 1436 (HY000): Thread stack overrun:

    今天搭私服的时候,卡在角色创建画面,日志报错如上. 这是MySQL报错ERROR 1436 (HY000): Thread stack overrun:   修改方法 vim /etc/my.cnf ...

  9. XRDP freerdp

    服务器上 freerdp 桌面上有XRDP

  10. power delivery功率输出

    delivery