manacher 算法学习笔记

时间:2021-08-25
本文章向大家介绍manacher 算法学习笔记,主要包括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