Manacher 学习笔记(持续更新中……)

在博客园食用更佳

结合模板题P3805 【模板】Manacher讲解。

概念

Manacher 算法用于解决最长回文子串问题,时间复杂度是简洁的 \(O(n)\)

思路

四个字,插扩借推。

回文串分两种,奇数和偶数。

但是 Manacher 算法比较暴力而又简介,于是它在每个字符之间都加上了 #(一般都是),使得所有回文串都被迫变成了奇数回文。

比如字符串 abba,它变成了 #a#b#b#a#

从上面这个例子我们知道两头也要加。

都知道求这种问题的最暴力思路是 \(O(n^2)\) 列举字串并 \(O(n)\) 判断是否回文,一共是 \(n^3\) 的复杂度。

但是还有一种 \(O(n^2)\) 的方法叫做中心扩张,就是说回文串不管是奇是偶总该有个对称中心,列举对称中心再一个一个往外扩张直到碰壁为止。

维护一个“当前已知最靠右的回文串”的中心 C 和右边界 R。当计算的新中心 i 落在 R 之内时,利用回文对称性,i 的初始半径可以直接“借用”对称点 2C-i 的半径(取它与到 R 距离的较小值),从而避免从头扩展。

每当完成一次中心扩展后,若新回文的右边界超过了当前的 R,就更新 CR,把“最右边界”不断向右推进。这使得后续的“借”能覆盖更多位置,保证整体比较次数为线性。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,m,ans,cnt,r[22000005];
char s1[11000005],s[22000005];

signed main()
{
	scanf("%s",s1+1);
	cnt=strlen(s1+1);
	s[++n]='~';
	for(int i=1;i<=cnt;++i)
	{
		s[++n]=s1[i];
		s[++n]='~';
	}
	for(int i=1;i<=n;++i)
	{
		if(i<=m+r[m]-1) r[i]=min(r[2*m-i],m+r[m]-i);
		else r[i]=1;
		while(i+r[i]<=n&&i-r[i]>=1)
		{
			if(s[i+r[i]]!=s[i-r[i]]) break;
			++r[i];
		}
		if(i+r[i]>m+r[m]) m=i;
		ans=max(ans,r[i]-1);
	}
	cout<<ans;
	return 0;
}
posted @ 2026-07-27 10:39  cath20  阅读(15)  评论(0)    收藏  举报