Manacher算法(最长回文子串问题)

Reference

这篇文章共参考了以下两位大佬的文章以及教材《ACM/ICPC算法基础训练教程》:

Manacher算法的详细讲解

Manacher算法

Manacher算法可用于计算一个字符串中的最长回文子串的长度,相比于传统的动态规划方法,Manacher算法的时间复杂度可以缩短至。

字符串预处理

在进行Manacher之前,要先对原始字符串进行预处理,具体方法是:在字符串首位以及每个字符之间添加一个原字符串中没有的字符,比如 '#' , '$', '^'等,下面的函数可以对原字符串预处理。

string ManacherString(string s){
     string res = "";
     for(int i = 0; i < s.length(); i++){
         res += '#';
         res += s[i];
    }
     res += '#';
     return res;
 }

在介绍Manacher算法之前,我们首先需要清楚以下几个概念

回文半径 radius

对于原字符串中的每一个字符,以这个字符为中心的最长回文串长度的一半向上取整就是其回文半径。
radius[i] = \lceil L/2 \rceil
在Manacher算法中我们需要一个回文半径数组 radius[] 来储存每一个字符的回文半径。具体来看下面的这个例子:

回文半径

以下标 7 为回文中心的回文串是:"#b#c#b#",长度为7,那么 radius[7] 就是4

最大回文右边界 right

所谓最大回文右边界就是:「以下标 i 及其之前的字符为回文中心的所有回文串所能达到的最大右边界」。举两个例子就能明白啦~


最大回文右边界的对称中心 C

顾名思义,就是最大回文右边界所对应的回文串的回文中心。还用之前的例子说明最右回文右边界的对称中心。

最大回文右边界的对称中心

Manacher算法

大体上,Manacher算法需要对处理过的字符串M中的每一个字符进行遍历,每一次遍历更新R,C,radius[] 参数。我们这里以遍历过程中下标 i 的位置进行分类讨论:

  • 第一种情况:下标 i 下一个位置是最大回文右边界的右边,也就是i > right

    在这种情况下,以当前下标 i 为对称中心,不断向左右两侧扩散检查左右两侧的字符是否相等,同时更新回文半径

radius[i] = 1;
     while(i + radius[i] < M.length() && i - radius[i] > -1){
       if(M[i - radius[i]] == M[i + radius[i]])
         radius[i]++;
       else break;
     }

最后更新最大回文右边界及对称中心。

  • 第二种情况:

1.下标 i 的下一个位置是最大回文右边界right或者小于最大回文右边界,并且对称中心C的回文左边界C_L小于当前下标 i 以C为对称中心的对称点i_s的回文左边界i_L
可能听起来会有点绕口,看了下面的图就明白啦~

在这种情况下,下标 i 处的回文半径就是i_L的回文半径啦~,由于 i 之前的所有下标的回文半径均已算出,所以就不用再使用向两侧扩散的方法去计算回文半径了,radius[i] = radius[i_s]i_s的计算方式也很简单,用对称中心的下标C减去当前下标 i 与对称中心C的差值就好啦,所以i_L = C-(i-C) = 2\times C - i

2.下标 i 的下一个位置不在最大回文右边界的右边,且C_L > i_L

这种情况由于下标 i 的对称点的长度超出了right - i,所以 i 的回文半径就是 right - i + 1

3.下标 i 的下一个位置不在最右回文串的右边,且C_L == i_L

这种情况下,我们无法确定下标 i 的回文右边界是否在最右回文右边界right之后,所以需要以 i 为中心向两端搜索其最大回文半径,不过因为已经保证区间[i,right]一定在下标 i 的回文半径范围内,所以只要从R开始搜索即可。

Manacher算法实现(C++)

string ManacherString(string s){
    string res = "";
    for(int i = 0; i < s.length(); i++){
        res += '#';
        res += s[i];
    }
    res += '#';
    return res;
}
int Manacher(string s){
    //radius : 以每一个位置为回文中心求出的回文半径长度
    //R : 最大回文右边界,以当前位置i及其之前的字符为回文中心的回文串所能达到的最右边界
    //C : 当前指针i之前且与R最近的回文中心
    if(s.length() == 0)return 0;
    string M = ManacherString(s);
    vector<int>radius(M.length());
    int R = -1; //最右回文右边界
    int C = -1; //最右回文右边界对称中心
    int sup = -0x3f3f3f3f;
    for(int i = 0; i < radius.size(); i++){
        radius[i] = R > i ? min(radius[2 * C - i], R - i + 1) : 1;
        while(i + radius[i] < M.length() && i - radius[i] > -1){ //从中心向两边扩寻找半径
            if(M[i-radius[i]] == M[i + radius[i]])radius[i]++;
            else break;
        }
        if(i + radius[i] > R){ //更新最大回文右边界及对称中心
            R = i + radius[i] - 1;
            C = i;
        }
        sup = max(sup, radius[i]);
    }
    return sup - 1;
}

习题

leetcode #5最长回文子串
PAT甲级1040

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容