【学习笔记】字符串—马拉车(Manacher)

一:【前言】

马拉车用于求解连续回文子串问题,效率极高。

其核心思想与 \(kmp\) 类似:继承。 ——引自 \(yyx\) 学姐


二:【算法原理】

对于任意一个回文串 \(a\),设其中点为 \(mid\)(为方便描述,偶数串则在正中央加一个位置),那么根据定义,有:

\(a[mid-1]==a[mid+1]\)

\(a[mid-2]==a[mid+2]\)

\(...\)

可知:

如果 \(a[mid-x]\) 可以形成半径为 \(r\) 的回文串,且不越过以 \(mid\) 为中心的回文串,那么 \(a[mid+x]\) 也可以形成半径为 \(r\) 的回文串。

以奇回文串为例,用 \(r[i]\) 表示以 \(i\) 为中点的最大回文串长度。

如果我们已知 \(r[mid],r[j](j \in [mid-r[mid]+1,mid-1])\),那么可以分两种情况推出其关于 \(mid\) 的对称点 \(i\) \((\frac {i+j}{2}=mid)\) 的半径:

设 \(R=mid+r[mid]-1\) 。

\((1).\) \(i+r[j]-1<=R,\) \(r[i]=r[j]\) 。

\((2).\) \(i+r[j]-1>R,\) \(r[i]=R-i+1\) 。

即 \(r[i]=min(r[j],R-i+1)\) 。

如上所述,实现了从前面信息到后面信息的继承

继承之后还需要判断以 \(i\) 为中心能否继续扩张,这时候可以直接暴力枚举扩大半径


三:【算法实现】

实时维护一个已知覆盖范围最靠右的回文串信息,记录其中点 \(mid\) 和右端点 \(R\)。

从 \(1,n\) 枚举 \(i\):

若 \(i<=R\) 则可以用 \(i\) 的对称点 \(mid*2-i\) 得到 \(r[i]\),

若 \(i>R\)(即 \(R=i-1\) 的情况,因为 \(R\) 永远大于等于 \(i-1\)),初始化 \(r[i]=1\),然后暴力扩大求出最大 \(r[i]\)。

如果以 \(i\) 为中点可以得到更靠右的回文串,更新 \(mid\) 和 \(R\)。

但偶数串不太好处理,所以在原字符串中的所有空隙中插入一个不可能出现的字符,例如 \('*',\) \('|'\) 等等,最前面和最后面也要插。

此时,奇数串还是奇数串偶数串则变成了奇数串,可以统一按照奇数串处理啦。

注意:统计答案时应取真实的回文串长度。

(具体可以自己画个图分类讨论一下,会发现无论奇偶,无论是否为原字符串中的字符,\(r[i]-1\) 始终等于以 \(i\) 为中点的实际最大回文串长度。)

【Code】

题目:\(Palindrome\) \([Poj3974]\)

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<string>
#define Re register int
using namespace std;
const int N=1e6+5;
int n,R,mid,ans,OOO,r[N<<1];char op[N],a[N<<1];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
int main(){
while(cin>>op){
if(op[0]=='E'&&op[1]=='N'&&op[2]=='D')break;//没用string就只能这样一位一位地判断了
a[0]='$',a[n=1]='*',R=0,mid=0,ans=0;
for(Re i=0;op[i];++i)a[++n]=op[i],r[n]=0,a[++n]='*',r[n]=0;//玄学填空法
for(Re i=1;i<=n;++i){
r[i]=i<=R?min(R-i+1,r[(mid<<1)-i]):1;//继承前辈的信息
while(a[i-r[i]]==a[i+r[i]])++r[i];//暴力扩张领域
if(i+r[i]-1>R)R=i+r[mid=i]-1;
ans=max(ans,r[i]-1);//取实际回文长度
}
printf("Case %d: %d\n",++OOO,ans);
}
}

四:【时间复杂度】

似乎看起来效率并不高,近似 \(O(n^2)\),但实际上它的理论复杂度是 \(O(n)\)。

为何?

对于每个 \(i\):

如果 \(i<=R\),那么直接 \(O(1)\) 计算 \(r[i]\)。

如果 \(i>R\),那么会用 \(O(len)\) 向右扫描 \(len\)个单位,所以此时 \(R\) 会向右移动 \(len+1\) 的单位。

而 \(R\) 最多只会移动 \(n\) 个单位,因此 \(Manacher\) 算法时间复杂度是线性的:\(O(n)\)。


五:【例题】

【学习笔记】字符串—马拉车(Manacher)的更多相关文章

  1. 《python基础教程(第二版)》学习笔记 字符串(第3章)

    <python基础教程(第二版)>学习笔记 字符串(第3章)所有的基本的序列操作(索引,分片,乘法,判断成员资格,求长度,求最大最小值)对字符串也适用.字符串是不可以改变的:格式化输出字符 ...

  2. 【Redis】命令学习笔记——字符串(String)(23个超全字典版)

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合). 本篇基于redis 4.0.11版本,学习字符串( ...

  3. es6学习笔记--字符串&数值&数组&函数&对象的扩展

    这几天抽空学习了es6语法,关于字符串,数值,数组,函数以及对象的扩展,看到es6标准入门这本书,里面讲的扩展特别多,我认为有几部分在项目上用不到,就挑有用的当笔记学习了. 字符串的扩展 str.in ...

  4. Java学习笔记--字符串和文件IO

    1.Java中的字符串类和字符的表示 2.区分String,StringBuilder和StringBuffer 3.从命令行中给main方法传递参数 4.文件操作 1 Java中的字符串和字符 1. ...

  5. Python学习笔记-字符串

    Python之使用字符串 1.所有的标准序列操作(索引,分片,乘法,判断成员资格,求长度,取最小值,最大值)对字符串同样适用.但是字符串都是不可变的. 2.字符串格式化使用字符串格式化操作符即%. f ...

  6. Java-J2SE学习笔记-字符串转化为二维数组

    1.字符串转化为二维Double数组 2.代码: package Test; public class TestDouble { public static void main(String[] ar ...

  7. Java学习笔记——字符串常用函数

    class JavaTest4_String { public static void main(String[] args) { String str1 = "IOS,ANDROID,BB ...

  8. C语言学习笔记--字符串函数

    字符串函数 需要包含头文件#include<stdio.h> strlen strcmp strcpy strchr strstr strcasestr

  9. 【学习】js学习笔记---字符串对象

    一.属性 1.length 字符串的长度,且不区分中英文的字节. 示例代码: var str="abcdefghijklmn"; var str1="中文汉语" ...

  10. Python学习笔记 - 字符串和编码

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- #第一行注释是为了告诉Linux/OS X系统, #这是一个Python可执行程序,Windows系统会忽 ...

随机推荐

  1. Windows下硬盘存储情况可视化工具--WinDirStat

    WinDirStat是一款免费的用于Windows下硬盘空间可视化工具. 下载地址:https://windirstat.en.softonic.com/ 界面如图所示: 可以轻易看出硬盘空间使用情况 ...

  2. Django restframework 序列化之 ModelSerializer 小记

    首先介绍一下相关概念 序列化器(Serializer) 1. 自定义型:  继承rest_framework.serializers.Serializer 2. 模型类型:  继承rest_frame ...

  3. composer install 出现“Please provide a valid cache path”

    本文背景:通过deployer部署PHP项目[deployer部署工具:https://deployer.org/] 问题:Php 的laravel框架中执行 composer install 后, ...

  4. Java使用MD5加密算法,实现等登陆功能

    Java实现MD5加密 为了保护有些数据,就需要采取一些手段来进行数据的加密,防止被别人破解. MD5简介 md5的全称是md5信息摘要算法(英文:MD5 Message-Digest Algorit ...

  5. 18、Apache服务器

    --   web 服务器   survey.netcraft.net     --此网站会有每月份的世界上网站使用的WEB服务器的使用率统计   www.apache.org     apache a ...

  6. 基于图的异常检测(三):GraphRAD

    基于图的异常检测(三):GraphRAD 风浪 一个快乐的数据玩家/风控/图挖掘 24 人赞同了该文章 论文:<GraphRAD: A Graph-based Risky Account Det ...

  7. 201871010102-常龙龙《面向对象程序设计(java)》第七周学习总结

    二.博文正文开头格式: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/ ...

  8. 2018710101110-李华《面向对象程序设计(java)》第十二周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  9. 283.移动零 关于列表list与remove原理*****(简单)

    题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 注意,该题目要求不开辟行的数组空间,在原数据上进行操作. 示例: 输入: [0,1,0,3,12 ...

  10. VIJOS-P1201 高低位交换

    洛谷 P1100 高低位交换 https://www.luogu.org/problemnew/show/P1100 JDOJ 1349: VIJOS-P1201 高低位交换 https://neoo ...