manacher 学习笔记(再回首)

发布时间 2023-06-07 17:15:44作者: frostwood

这一算法用于求最长回文子串。

思想上和 KMP 类似,都是利用已求出的部分去减少不必要的枚举。

我们设 \(f_i\) 表示以 \(i\) 为中心的最长回文子串长度。假设现在有一个以 \(Q\) 为中心的回文子串,其右边界为 \(mr\),现在需要去求 \(Q\) 点右侧一点 \(p\) 所对应的 \(f_p\),我们设 \(d\)\(p\) 关于 \(Q\) 的对称点,根据回文串的性质,那么有 \(f_p = min(mr-i, f_d)\)。如果到达边界的时候尝试扩展就好了。注意边界条件的判定。

我们又发现,上述过程只能针对长度为奇数的串。那如何处理偶数串呢?我们只需要在原串的缝隙间添上一些无用字符即可。

代码:

int n;
int f[N*2];
char s[N*2], a[N];
int mx;
void manacher()
{
	s[0] = '!', s[1] = '#';
	for(int i = 1; i<=n; i++)
	{
		s[i<<1] = a[i];
		s[(i<<1)|1] = '#';
	}
	n = (n<<1)|1, s[n+1] = '/';
	for(int i = 1, mr = 1, mid = 0;i<=n; i++)
	{
		f[i] = i<mr?min(mr-i, f[mid*2-i]):1;
		while(s[i+f[i]]==s[i-f[i]]) f[i]++;
		if(i+f[i]>mr) mr = i+f[i], mid = i;
	} 
	for(int i = 1; i<=n;i++)
	{
		mx = max(f[i]-1, mx);
	}
}