Mannacher 算法

Mannacher 算法

马拉车算法(中文翻译,有点搞笑)主要是用来求解字符串的最大回文字串。回文串是一个很常出现的东西,求解字符串的回文串也是很经典的题目,马拉车算法是一个可以在 O ( n ) O(n) O(n)复杂度内求解字符串的最大回文串的算法。

基本介绍

在求解前我们要先对该字符串做一些预处理,其过程为: 在字符串的最前面加上"@#", 当然这两个符号都不能出现字符串当中,之后在字符串每个字符后面加上一个"#"; 举个例子,字符串"abcba"处理之后变成了"@#a#b#c#b#a#"; 这样做的目的是为了方便后面的讨论(不用分奇偶讨论);

该算法需要计算一个数组 p p p, 表示以每个位置为中心的字符的最大回文半径。两个辅助变量 m x mx mx i d id id, 分别表示以 i d id id为中心回文串的最大边界 m x mx mx
算法的核心步骤:
p [ i ] = m x > i ? m i n ( p [ 2 ⋅ i d − i ] , m x − i ) : 1 p[i]=mx>i? min(p[2\cdot id-i], mx-i):1 p[i]=mx>i?min(p[2idi],mxi):1
这个公式的意思为 m x > i mx>i mx>i时, p [ i ] = m i n ( p [ 2 ⋅ i d − i ] , m x − i ) p[i]=min(p[2\cdot id -i], mx-i) p[i]=min(p[2idi],mxi), 否则 p [ i ] = 1 p[i]=1 p[i]=1

推导过程:
j j j是i关于 i d id id对称的点,则 j = 2 ⋅ i d − i j=2\cdot id-i j=2idi. 此时以 i d id id为对称的中心的最大的半径为 m x mx mx(也是当前回文串能延伸的最远处).
(1) 当 m x ≤ i mx \leq i mxi
此时显然对 i i i单独考虑,一个一个去匹配。
(2) m x > i mx>i mx>i
( i ) (i) (i) m x − i > p [ j ] mx-i>p[j] mxi>p[j]
此时以 p [ j ] p[j] p[j]为中心的最大回文串在以 i d id id为中心的回文串内,又因为回文串的对称性,所以 p [ i ] = p [ j ] = p [ 2 ⋅ i d − i ] p[i]=p[j]=p[2\cdot id-i] p[i]=p[j]=p[2idi].
( i i ) (ii) (ii) m x − i ≤ p [ j ] mx-i \leq p[j] mxip[j]
此时以 p [ j ] p[j] p[j]为中心的最大回文串半径大于等于 m x − i mx-i mxi, 而在 m x mx mx之外的则需要单独寻找.

代码:

//  小学生一发的刷题之路
//
//  最大回文串-马拉车算法
//
//

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <deque>                //双向队列;
#include <cmath>
#include <set>
#include <stack>
#include <map>
#include <vector>
#include <cstdlib>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double PI=acos(-1.0);
const double eps=1e-8;
const int maxn=1e5+5;
const int maxm=1e3+5;
const ll mod=1e9+7;
const int INF=1e8;
template<class T>
inline void read(T &ret){       //快速输入模版;
    ret=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        ret=ret*10+c-'0';
        c=getchar();
    }
    ret*=f;
}
template <class T>
inline void out(T ret){     //快速输出模版;
    if(ret>9)
    {
        out(ret/10);
    }
    putchar(ret%10+'0');
}
string str;
int p[maxn];

void Manacher(string str){
    //字符串的预处理;
    string new_str="s#";
    for(int i=0;i<str.size();i++){
        new_str.push_back(str[i]);
        new_str+="#";
    }
    
    memset(p,0,sizeof(p));
    int mx=0,id=0,resLen=0,resCenter=0;
    for(int i=1;i<new_str.size();i++){
        p[i]=mx>i?min(p[2*id-i],mx-i):1;
        while(new_str[i+p[i]]==new_str[i-p[i]]){        //匹配相等的;
            p[i]++;
        }
        if(mx<i+p[i]){
            mx=i+p[i];
            id=i;
        }
        if(resLen<p[i]){
            resLen=p[i];
            resCenter=i;
        }
    }
    
    //最大回文串的开头位置为新字符串的中心位置减去半径长/2;
    //回文串的长度为半径长减1;
    int maxLen=resLen-1,st=(resCenter-resLen)/2;
    for(int i=st;i<st+resLen-1;i++){
        cout<<str[i];
    }
    cout<<endl;
}

int main()
{
    cin>>str;
    Manacher(str);      //马拉车算法求最大回文字串;
    return 0;
}

新的开始,每天都要快乐哈!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值