manacher算法理解

前言:kmp是预处理最大前后缀的思路来跳转,马拉车利用了回文子串的半径来达到减少冗余。

如何求解一串字符串中的最长回文子串。

1.暴力O(n^3)

枚举字符串再check

2.暴力O(n^2)

枚举对称中心,然后两个指针由此为中心拓展,更新答案,O(n^2)

3.二分+哈希O(nlogn)

由于一个回文串,知道了对称中心后,左边的串和右边的相同。因此其实存在单调性。

S【i...j】为回文串,则S【i+1,j-1】为回文串。因此可以二分半径长度。而字符串的判定可以哈希来做。

但是有时候实际用起来比较容易调边界,并且单哈希可以卡,log常数如果拉到1e7级别的基本是过不来了的。

4.马拉车O(n)

回文串的特征是中心对称。开一个p[i]数组来预处理以i为对称中心的最大半径。同时以pos记录中点,maxr记录当前的最长回文子串的最右端点。

如果一个回文串对称中心的一侧有另一个回文子串,那么对称过去也有一个相同长度的回文子串。

manacher的思路就是以此优化,减少暴力的寻找。

( 图源https://www.luogu.com.cn/user/53930 )

在这里插入图片描述

黄色部分完全一致,这就是manacher的核心。

如上图的情况,i<maxr。黄色部分完全一致。此时可以直接更新p[i]=p[j]。

或者如下面的情况,j的p[j]长度过大导致越界。因此i此时更新可以保证的范围是maxr-i这一段长度。

所以每一次若i在<maxr的范围内,我们要在这两者的中取min。

同时要注意,当p[i]更新完后,i点仍然可能继续更新产生更大半径。因此要继续更新该点的半径。但是此时已经更新了一部分p[i]。直接从i-p[i]-1与i+p[i]+1的位置开始比较这两端的字母。大大地优化了一段距离的重复查找。

在这里插入图片描述

而如下者情况,我们无法保证i的最大半径。所以按照上述暴力更新。

在这里插入图片描述 

 当前更新完了之后。判断i+p[i]是否超过maxr。超过后就可以更新maxr,同时将pos移到i位置。如此往复下去。

模板题:https://www.luogu.com.cn/problem/P3805

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e7+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
char s[maxn*2],ss[maxn*2];
int p[maxn*2],maxr,pos;
void manacher(char* str,int len)
{
    for(int i=1;i<=len;i++)
    {
        if(i<maxr) p[i]=min(p[(pos<<1)-i],maxr-i);
        while( str[i-p[i]-1 ] == str[i+p[i]+1]  ) ++p[i];
        if(i+p[i]>maxr) maxr=i+p[i],pos=i;
    }
}
int main(void){
    scanf("%s",s+1);int n=strlen(s+1);
    ss[0]='@';ss[1]='#';
    for(int i=1;i<=n;i++) ss[i<<1]=s[i],ss[i<<1|1]='#';

    strcpy(s,ss);
    manacher(s,strlen(s));
    int ans=1;
    n=(n<<1)|1;

    for(int i=1;i<=n;i++)
    {
        ans=max(ans,p[i]);
    }
    cout<<ans<<"\n";
   	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值