manacher 算法学习笔记
学习笔记\({}-\tt manacher\) 算法(马拉车)
\(\color{red}\xcancel{\small\color{black}\textsf{马拉车}}\)
\(\small\color{black}\textsf{没马拉车}\color{green}\surd\)
问题
求出一个字符串中最长回文串的长度。
暴力
最直观的思想(雾
枚举起点,重点,判断是否回文,总复杂度 \(\mathcal O(n^3)\)
改进
枚举对称点,向左右扩散,复杂度 \(\mathcal O(n^2)\)
缺点
- 对称点可能是一个位置,可能是两个位置中间,不方便暴力
- 有许多位置被重复搜索(显然,本来就是由内向外搜)
manacher
对称点
对于对称点不方便的问题,算法的解决方案是 在每两个位置插入一个相同字符,比如 \(\#\) 或者 $$ $ 或者 \(∼\)。这样对称点就会在 原来字符或者新加字符。
同样,为了防止比对出界,在开头也要另插入一个字符。
114514
\(\to\) ~~1~1~4~5~1~4~
或 ##1#1#4#5#1#4#
比对
事先定义 \(pa_i=\) 以 \(i\) 开头的回文串的长度。事实上\(pa=\tt Paralindone\)。
对于重复搜索的问题,算法有两个辅助变量 \(maxr\) 和 \(mid\)。前者代表 \(\max\{i+pa_i\}\),后者代表 \(i+pa_i\) 最大时的 \(i\)。
分两种情况讨论。
情况1 \((i\le maxr)\)
考虑已经算出 \(pa_k(1\le k\lt i)\),此时我们要求 \(pa_i\)。
\(mid,maxr\) 的位置如图所示,\(p\) 是 \(maxr\) 对于 \(mid\) 的对称点,也就是 \(mid-pa _ {mid}\),\(j\) 是 \(i\) 对于 \(mid\) 的对称点。
我们找到 \(j\) 以及它的对称位置:
发现 \(j\) 的左对称点,已经超过了 \(p\),可是 \(i\) 的对称位置会不会超过 \(maxr\) 呢?
不难发现,从对称部分最右边开始,可以通过回文两侧相等的性质,一步步传递到最左端的青色
那么 \(s _ {p-1}=s _ {maxr+1}\),\(maxr\) 应该等于 \(maxr+1\),所以不可能。
既然当 \(j\) 回文的最左部分超过 \(p\) 时就不可行了,此时答案就为 \(j-p\) 或者 \(maxr-i\)
最后答案 \(pa_i=\min(pa_j,j-p)\)。
为什么?
- \(pa_j\) 出界,此时 \(pa_j\gt j-p\),答案 \({}=j-p\)
- \(pa_j\) 未出界,此时 \(pa_j\le j-p\),答案 \({}=pa_j\)
最后从 \(i\pm pa_i\) 开始一位一位比较拓展 \(pa_i\)。
情况 2 \((i\gt maxr)\)
此时 \(i\) 已经超出了我们最远匹配到的位置,所以初始化 \(pa_i=1\),然后一位一位开始拓展。
两种情况最后都要更新 \(maxr\) 与 \(mid\)!!!
Code
#include<stdio.h>
#include<string.h>
const int max_n = 1e7 + 1e6 + 1;
const int maxn = 2e7 + 2e6 + 1;
const int& max2(const int& a,const int& b){return a > b ? a : b;}
const int& min2(const int& a,const int& b){return a < b ? a : b;}
char s2[max_n],s[maxn],pa[maxn];
int n;
void chg(){// 处理字符串以方便匹配
int now = 0;
s[now] = '#',s[++now] = '#';// 插入从未出现的字符
for(int i = 0;i < n;++i) s[++now] = s2[i],s[++now] = '#';
n = now;
}
void Horseless_pull_car(){// 没马拉车
int maxr = 0,mid;
for(int i = 1;i < n;++i){
pa[i] = i < maxr ? min2(pa[mid * 2 - i],pa[mid] + mid - i) : 1;// j-p=maxr-i
for(;s[i + pa[i]] == s[i - pa[i]];++pa[i]);// 一位一位匹配
if(pa[i] + i > maxr) maxr = pa[i] + i,mid = i;// 更新 maxr,mid
}
}
int main(){
scanf("%s",s2);
n = strlen(s2);
chg();
Horseless_pull_car();
int res = 1;
for(int i = 0;i < n;++i) res = max2(res,pa[i]);
printf("%d\n",res - 1);// 由于答案是+1存的,因此要-1
return 0;
}
原文地址:https://www.cnblogs.com/zeno6174/p/notes-of-manacher.html
- hduoj-----(2896)病毒侵袭(ac自动机)
- MySQL数据库(二):基本管理
- Golang编程实现生成n个从a到b不重复随机数的方法
- TiDB 在株式会社 FUNYOURS JAPAN 的应用
- MySQL数据库(三):数据类型
- spark开发环境详细教程2:window下sbt库的设置
- hdu----(2222)Keywords Search(ac自动机)
- MySQL数据库(四):约束条件
- hdu----(2084)数塔(dp)
- golang简单tls协议用法完整示例
- spark开发环境详细教程1:IntelliJ IDEA使用详细说明
- MySQL数据库(五):索引
- hdu----(1466)计算直线的交点数(dp)
- golang模板template自定义函数用法示例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法