O(n)回文子串(Manacher)算法

来源:互联网 发布:罗辑思维 人工智能 编辑:程序博客网 时间:2024/05/21 19:27

回文子串

问题描述:

输入一个字符串,求出其中最大的回文子串。子串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同,如abba和yyxyy。

 

解析:

这里介绍O(n)回文子串(Manacher)算法

算法基本要点:首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。

下面以字符串12212321为例,经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";

然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:

S     #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P     1   2  1  2  5   2  1  4   1  2  1  6   1  2   1  2  1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

下面计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

这个算法的关键点就在这里了:如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。

具体代码如下:

复制代码
if(mx > i){      p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));}else{       p[i] = 1;}
复制代码

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。

当 P[j] > mx - i 的时候,以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能一个一个匹配了。

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了

下面给出原文,进一步解释算法为线性的原因

 

源代码:

复制代码
#include <iostream>#include <string>#include <cstring>using namespace std;void findBMstr(string& str){    int *p = new int[str.size() + 1];    memset(p, 0, sizeof(p));    int mx = 0, id = 0;    for(int i = 1; i <=  str.size(); i++)    {        if(mx > i)        {            p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));        }        else        {            p[i] = 1;        }        while(str[i - p[i]] == str[i + p[i]])            p[i]++;        if(i + p[i] > mx)        {            mx = i + p[i];            id = i;        }    }    int max = 0, ii;    for(int i = 1; i < str.size(); i++)    {        if(p[i] > max)        {            ii = i;            max = p[i];        }    }    max--;    int start = ii - max ;    int end = ii + max;    for(int i = start; i <= end; i++)    {        if(str[i] != '#')        {            cout << str[i];        }    }    cout << endl;    delete  p;}int main(){    string str = "12212321";    string str0;    str0 += "$#";    for(int i = 0; i < str.size(); i++)    {        str0 += str[i];        str0 += "#";    }    cout << str0 << endl;    findBMstr(str0);    return 0;}
复制代码


执行结果:

人一我百!人十我万!永不放弃~~~怀着自信的心,去追逐梦想。


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 去韩国不懂韩语怎么办 一类在会计分录怎么办 想去韩国整容怎么办 抄经写错字怎么办贴吧 佛经抄错了怎么办 踏板摩托声音大怎么办 法语面签听不懂怎么办 常州出生证丢了怎么办 分公司就俩个人怎么办 分公司就三个人怎么办 转账支票作废后怎么办 干组织不是党员怎么办 宾馆客人逃房费怎么办 cad图太大打不开怎么办 cad文字输入不了怎么办 word打不了汉字怎么办 电脑浏览器卡顿怎么办 文档里输入不了怎么办 淘宝退款卖家不处理怎么办 抵押合同丢了怎么办 抵押合同丢失了怎么办 发票货物名称多怎么办 发票上少打一个字怎么办 发票名称带星号怎么办 小贷太多还不上怎么办 生日当天买保险怎么办 我挪用公司货款怎么办 车辆改名字保险怎么办 工伤报案周六日怎么办 五菱宏光s1门下沉怎么办 新手机版本更新怎么办 戴尔电脑开机黑屏怎么办 淘宝退货不发货怎么办 铝被酸腐蚀怎么办 新娘头饰氧化了怎么办 合金饰品变黑了怎么办 麻醉机fico2升高怎么办 快递被恶意投诉怎么办 顺风快递收件人拒收怎么办 手机联系人没了怎么办 收件人号码错了怎么办