[笔记]Manacher 算法

Manacher 算法可以在 \(O(n)\) 时间内求得一个字符串的最长回文子串。

比如 baka 的最长回文子串为 aka

板:P3805 【模板】Manacher

由于奇、偶数长度的回文串形态不同,为了避免分类讨论,我们在字符之间添加特殊字符如 #,这样就只需要考虑奇数长度了。

另外,我们需要在字符串开头添加另一个特殊字符如 $,原因见后面的代码。

\(d_i\)\(i\) 为中心扩展出的最长回文半径。再记录 \(r\) 为目前最长回文子串的右端点,\(mid\) 为它的回文中心。

分两种情况讨论:

  • \(mid<i<r\)

    \(j\)\(i\) 关于 \(mid\) 的对称点,则有 \(S[j-d_j+1,j+d_j-1]=S[i-d_i+1,i+d_i-1]\)

    然而在 \(i+d_i-1>r\) 时,我们不能保证上式成立。所以应当让 \(d_i=\min(d_j,r-i+1)\),再暴力向右扩展。

    image

  • \(i\ge r\)

    没办法利用以前的信息,暴力向右扩展即可。

考虑分析这样的时间复杂度:

  • \(min<i<r\) 时,设置 \(d_i\) 的初值是 \(O(1)\) 的,每次扩展会让 \(r\) 增加 \(1\)
  • \(i\ge r\) 时,每次扩展会让 \(r\) 增加 \(1\)

\(r\) 的增加是 \(O(n)\) 的,所以总时间也是 \(O(n)\) 的。

const int N=1.1e7+10;
int n,d[N<<1];
string tmp,s;
void get_d(){
	d[1]=1;
	for(int i=2,l,r=1;i<=n;i++){
		if(i<=r) d[i]=min(d[r-i+l],r-i+1);
		while(s[i-d[i]]==s[i+d[i]]) d[i]++;//开头添加 $ 是为了防止这里越界
		if(i+d[i]-1>r) l=i-d[i]+1,r=i+d[i]-1;
	}
}
signed main(){
	cin>>tmp;
	s="$#";
	for(char i:tmp) s+=i,s+='#';
	n=s.size()-1;
	get_d();
	cout<<*max_element(d+1,d+1+n)-1;
	return 0;
}

例题

P5446 [THUPC 2018] 绿绿和串串

\(f_i\)\(i\) 能否作为一个合法的前缀,\(d_i\) 为以 \(i\) 为中心的最长回文半径。

分讨一下:

  • \(i+d_i-1=n\)

    这意味着一次翻转即可覆盖 \(S\),自然 \(i\) 可以作为合法的前缀,\(f_i=1\)
    image

  • \(i-d_i+1=1\)

    这意味着需要多次旋转才可能覆盖 \(S\),此时 \(f_i=f_{i+d_i-1}\)
    image

时间复杂度 \(O(n)\)

实现细节上:

  • 注意多测清空,比如 \(f[1]\) 可能被自己转移到,所以上一轮的 \(f[1]\) 必须提前清空。
  • 本题不需要求偶数长度的回文串,所以不需要在字符间补充 #;但是需要在开头和结尾补充不同的特殊字符。开头加是防止越界,结尾加是防止上一轮的字符串影响。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int t,n,d[N];
bitset<N> f;
string s;
inline void get_d(){
	d[1]=1;
	for(int i=2,l=0,r=1;i<=n;i++){
		if(i<=r) d[i]=min(d[r-i+l],r-i+1);
		else d[i]=1;//多测清空 
		while(s[i-d[i]]==s[i+d[i]]) d[i]++;
		if(i+d[i]-1>r) l=i-d[i]+1,r=i+d[i]-1;
	}
}
signed main(){
	cin>>t;
	while(t--){
		cin>>s;
		n=s.size();
		s='#'+s+'$';
		get_d();
		f[1]=0;//多测清空 
		for(int i=n;i;i--){
			if(i+d[i]-1==n) f[i]=1;
			else f[i]=(f[i+d[i]-1]&&i==d[i]);
		}
		for(int i=1;i<=n;i++) if(f[i]) cout<<i<<" ";
		cout<<"\n";
	}
	return 0;
}

此题也有字符串哈希写法,原理类似。

\(\text{-\quad to be continued\quad -}\)

posted @ 2025-10-31 09:43  Sinktank  阅读(10)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.