【学习笔记】字符串—马拉车(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. 高强度学习训练第四天总结:JVM+Redis

    JVM 复习了JVM堆内存的几个模块. 复习了JVM的几个控制工具. 复习了JVM发展历史 Redis 复习了Redis的事务控制.

  2. 关于创建node服务

    1.环境条件准备: A.确定node已经创建 B.npm或cnpm已经下载,npm和cnpm其实是一个道理 C.mysql或者使用其他数据库已经安装(本例使用mysql) 2.开始创建,首先新建一个文 ...

  3. SQLServer修改表名、修改列名

    基本语法 修改表名:EXEC sp_rename ‘原有表名’, '新表名'; 修改列名:EXEC sp_rename ‘表名.[原有列名]’, ‘新列名' , 'COLUMN'; EXEC sp_r ...

  4. 渗透常见linux命令

    在拿到一个 webshell 之后,大家首先会想到去把自己的权限提升到最高,windows 我们会提升到 SYSTEM 权限,而 Linux 我们会提升到 root 权限,拿在进行 Linux 提权的 ...

  5. Valgrind调试

    Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Google-O'Reilly开源代码奖 摘自 Valgrind.org: Valg ...

  6. Git内部原理浅析

    Git独特之处 Git是一个分布式版本控制系统,首先分布式意味着Git不仅仅在服务端有远程仓库,同时会在本地也保留一个完整的本地仓库(.git/文件夹),这种分布式让Git拥有下面几个特点: 1.直接 ...

  7. Nginx内置变量以及日志格式变量

    $args #请求中的参数值$query_string #同 $args$arg_NAME #GET请求中NAME的值$is_args #如果请求中有参数,值为"?",否则为空字符 ...

  8. python27期day05:字典、字典嵌套、作业题。

    1.字典是python中的数据类型之一.唯一一种大括号{}键值对的数据. 2.存储大量的数据.将数据和数据之间进行关联. 3.通过键可以准确的找到值 4.哈希:可变数据类型就不可哈希   不可变数据类 ...

  9. python预科前三天:计算器知识、Python下载和安装、Pycharm下载安装激活设置、解释型和编译型、git、思维导图、显示隐藏文件、隐藏已知文件扩展名、创建组织、创建项目、提交作业、排BUG技巧

    1.计算机组成结构:CPU.硬盘.内存.输入输出设备.主板.电源. 2.硬件之间的协作关系:是CPU运算完后给操作系统.专业术语叫指令. 3.键盘输入a之后发生的事情:键盘-CPU-操作系统-显卡-显 ...

  10. 【PL/SQL】PLSQL Developer注册码

    [12.0.7] product code: 4vkjwhfeh3ufnqnmpr9brvcuyujrx3n3le serial Number:226959 password: xs374ca