manacher算法

2021/7/4 17:26:21

本文主要是介绍manacher算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

manacher 算法是用于解决字符串中最长回文子串问题的著名算法。

给定包含小写拉丁字母的字符串 \(S\),求其最长回文子串的长度。

首先想到暴力解法,枚举左右端点(\(O(n^2)\)),再 \(O(n)\) 判断是不是回文串,复杂度 \(O(n^3)\)。

优化的暴力:枚举回文串中心对称点。然而,当回文串的长度是偶数时,这个对称点该如何表示呢?

考虑在 \(S\) 的首尾及每两个字母中间添加通配符 #,这样,当回文串的长度是奇数时,对称点就在字母位置,反之在井号位置;如果我们发现了一个半径为 \(l\) 的回文串,它的真实长度就是 \(l-1\)。这里,回文半径指对称中心到串的一端的字符数(包括中心和端点),例如 #a#b#a# 半径为 \(4\),#a#b#b#a# 半径为 \(5\)。枚举中心点,再枚举一端,向两边扩展,复杂度 \(O(n^2)\)。

manacher 算法

设在加工后的串 \(S'\) 中,以 \(i\) 为回文中心的最长回文子串半径为 \(p_i\),从 \(1\) 到 \(n\) 顺序枚举 \(i\),并考虑借助 \(p_{1\sim i-1}\) 得到 \(p_i\)。同时维护 \(mx,id\) 分别表示 \(1\sim i-1\) 中回文串的右端所能到达的最远下标,和能达到此的回文子串的中心。

考虑分类讨论:image-20210704153106350.png

容易求出 \(mx\) 关于 \(id\) 的对称点 \(mx'=id-(mx-id)=2\cdot id-mx\),同理可以求出 \(i\) 关于 \(id\) 的对称点 \(j\)。

  1. 当 \(i+p_j-1<mx\) 时:由于 \(i,j\) 都在 \(mx'\sim mx\) 范围内,所以绿色区域内的所有信息都是左右相似的,在当前情况下,可以得到 \(p_i=p_j\)。

  2. 当 \(i+p_j-1\ge mx\) 时:我们还需要考虑绿色区域之外的 \(i\) 的回文串的组成部分,这一部分暴力对称判断。

    for k mx+1→n
    	if s[k]==s[i*2-k]
        	k←k+1
    k←k-1;
    p[i]←k-i+1;
    mx←k	id←i
    

复杂度分析

暴力判断的过程才是真正占复杂度的,而暴力判断的过程其实是在将 \(mx\) 向右移。在整个过程中,\(mx\) 共移了 \(O(n)\) 次。

【模板】manacher 算法

#include<bits/stdc++.h>
using namespace std;
const int N=1.1e7+10;
int p[N*2];
string str,s="#";
int main()
{
	cin>>str;
	int n=str.length();
	for(int i=0;i<n;i++)s+=str[i],s+='#';
	n=s.length();
	p[0]=1;
	int mx=0,id=0,ans=0;
	for(int i=1,j;i<n;i++){
		if(mx>i&&mx>p[id*2-i]+i-1)p[i]=p[id*2-i];
		else {
			for(j=mx+1;j<n&&s[j]==s[i*2-j];j++);
			j--;
			p[i]=j-i+1;
		}
		if(mx<i+p[i]-1)mx=i+p[i]-1,id=i;
		ans=max(ans,p[i]-1);
	}
	cout<<ans<<endl;
}


这篇关于manacher算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程