ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Manacher(马拉车)

2021-10-08 15:03:05  阅读:169  来源: 互联网

标签:子串 字符 int Manacher Max 拉车 回文


Able was I ere I saw Elba.     ----Napoléon Bonaparte(拿破仑)

一、回文串&回文子串

   这个很好理解。

  如果一个字符串正着读和反着读是一样的,那它就是回文串。 eg. abba ;

  如果一个字符串 S 的子串 SS 为回文串,那么 SS 即为 S 的回文子串;若 SSS 为 S 的回文子串中最长的一个,那么我们称 SSS 为 S 的最长回文子串

 

二、Manacher算法

  如何找到一个字符串的最长回文子串呢?

  我们很容易想到一个 O(n2) 的方法,即:从每个字符开始向两边爆搜。但显然,这个方法效率太低下了。

  如何快速求出答案,这就是 Manacher算法的事了。

 

  Ⅰ. 奇回文&偶回文

    字面意思,不再赘述。

    如果同时存在奇回文和偶回文,那么处理起来会比较的繁琐,下面就是Manacher 一个很巧妙的方法了:在字符串收尾,即各字符间插入一个特殊的字符(指没有出现过的字符),例如:

 

aba  ----> #a#b#a#
abba ----> #a#b#b#a#

 

    这样,所有的回文串就都成奇回文了。

 

inline int Pre()
{
    S[0]='@',S[1]='#';
    int j(1);
    for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='#';
    S[++j]='\0';return j;
}
预处理

 

 

  Ⅱ. 最长回文半径

    令一个回文串中最左或最右位置的字符与其对称轴的距离称为 " 回文半径 ",令 R [ i ] 表示字符 i 的最长回文半径。

i 0 1 2 3 4 5 6 7 8 9 10
S @ a # b # b # a # b $
R[i] 边界 1 1 2 4 2 1 4 1 2 边界

    显然,R [ i ] - 1 即为以 i 为中心的最长回文子串的长度。

    那么,我们要求的最长回文子串,就成了 max { R [ i ] - 1 } 。

    如何快速求出 R [ ] ???

 

  Ⅲ. Manacher

    从左往右依次讨论。

    设 Max 为已讨论的子串中能达到的右端最大位置,P 为提供当前 Max 的字符位置。如下表:

i 0 1 2 3 4 5 6 7 8 9 10 11  12  13  14  15 16 17 18 19 20
          Max的对称点                  Max            

    

    接下来讨论的位置 i 可以分为两种情况:小于等于 Max  ,大于 Max:

      1. 小于等于 Max :

i 0 1 2 3 4 5 6 7 8 9 10 11  12  13  14  15 16 17 18 19 20
          Max的对称点     j(i的对称点)       i      Max            

      由于 i 和 j 对称 ,所以求出 i 的对称点 j ,简直是 "轻易而举" 。

      

      不难发现现在有可以分为两种情况讨论:

          ( 1 ) . 以 j 为对称轴的回文串比较短:那么可以直接令 R [ i ] = R [ j ] ;

          ( 2 ) . 以 j 为对称轴的回文串比较长:此时,我们只能确定不超过 Max 的部分的情况,对于 Max 以外的,我们需要以 i 为中心开始往两侧拓展,直到两侧不同,同时更新 p 和 Max。

       

       2. 大于 Max :  这种情况很好处理,直接拓展就可以了(当然,要同时更新 p 和 Max)

 

   Ⅳ. 代码

 

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
char S[50000005];
int m,R[50000005];
string IP;
inline int Pre()
{
    S[0]='@',S[1]='#';
    int j(1);
    for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='#';
    S[++j]='\0';return j;
}
inline int Manacher()
{
    m=Pre();
    int RET(-1),x(0),Max(0);
    for(register int i=1;i<m;++i)
    {
        if(i<Max) R[i]=min(R[2*x-i],Max-i+1);
        else R[i]=1;
        while(S[i-R[i]]==S[i+R[i]]) ++R[i];
        if(Max<i+R[i]-1) x=i,Max=i+R[i]-1;
        RET=max(RET,R[i]-1);
    }
    return RET;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>IP,cout<<Manacher();
    return 0;
}
Manacher

 

 

  Ⅴ. 时间复杂度

    由于Max是不断向右拓展的,最多拓展 n 次,不难得出 马拉车 的时间复杂度是线性的,即 O( n )。

 

标签:子串,字符,int,Manacher,Max,拉车,回文
来源: https://www.cnblogs.com/qfxlzhe/p/15253194.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有