寻找最大回文字符串: Manacher算法详解

Manacher算法是一种用于找出给定字符串中最长的回文字符子串的算法.该算法的神来之笔是: 用一个不会出现在该目标字符串中的特殊字符对目标字符串进行填充, 为描述简便, 我们就假设该特殊符号为"#", 并且算法是从字符串的左端向右端开始寻找.

我们这样来完成填充:

1)在目标字符串的每个字符前面都塞入一个"#";

2)在目标字符串的末端加一个"#".

如此一来, 任意字符串"***"填充之后都会变成这个样子"#*#*#*#"

从填充的两个步骤可以看出, 任意长度为n的字符串, 在填充之后长度都会变成n+n+1, 即2n+1, 也就是说填充之后得到的字符串的长度一定是奇数.

由于后面不可避免地要讨论字符的位置下标, 所以从这里开始, 我们就把被填充后的字符串看做一个字符数组, 如果被填充前的目标字符串长度为n, 那么该字符数组的下标显然就是从0开始, 到2n结束. 同时为了后续描述的统一和便于理解, 我们在这个字符数组的基础上定义几个代名词:

当前有效回文字符串: 它是指当前已经找到的, 拥有最大右端下标的回文字符串, 注意, 它不一定是已经找到的最长回文字符串.

mPoint: middle point, 它是指当前有效回文字符串的中点字符的下标. mPoint的初始值是0, 它会逐渐右移.

radius: 回文字符串半径, 由于回文字符串是关于中点位置对称的, 所以把长度为n的回文字符串的半径定义为从中点位置分割后, 任意一侧的字符串长度+1(要把中点位置的字符算进去), 即n/2 + 1. 请注意: Manacher算法第一步所做的填充, 其神奇之处在这里开始显现, 因为从填充后的字符串中找出的最长回文字符串的长度也一定是奇数, 并且radius-1就是原始字符串中回文字符串的长度, 比如:

原字符串是"bbad", 我们可以找到的最长回文字符串是长度为2(偶数)的"bb"

而由于填充之后的字符串是"#b#b#a#d#", 所以我们可以找到的最长回文字符串就变成了长度为5(奇数)的"#b#b#" (半径为3, 减1恰好等于2)

原字符串是"cabad", 我们可以找到的最长回文字符串是长度为3(奇数)的"aba"

而由于填充之后的字符串是"#c#a#b#a#d#", 所以我们可以找到的最长回文字符串就变成了长度为7(仍为奇数)的"#a#b#a#" (半径为4, 减1恰好等于3)

数组P: 构造一个数组P, 用于记录字符数组中每个元素以其为中点可以形成的最大回文字符串的半径

rPoint: right point, 它是指当前有效回文字符串的右端字符的下标+1(注意我们是从左端开始检查的), rPoint = mPoint+P[mPoint]. rPoint的作用是利用回文字符串的特性, 减少不必要的检查, 后面会详述. rPoint的初始值是0, 它会逐渐右移

Manacher算法开始:

第一步, 初始化:

令 i = 0 (当前检查的字符下标), mPoint=0, rPoint = 0

第二步:

判断不等式①i < rPoint , 亦即确认当前检查的字符是否落在了当前有效回文字符串内

如果不等式①不成立, 说明当前还没有找到回文字符串, 或者当前有效回文字符串的半径没有覆盖到当前检查的字符, 则令radius = 1开始检查, 由于radius = 1, 即只包含当前元素本身, 必然立即找到以当前字符为中点第一个回文字符串(即它自己), 更新P[i] = 1

如果不等式①成立, 说明之前的检查已经找到了当前有效回文字符串, 并且其半径覆盖到了当前检查的字符, 也可以说当前检查的字符落在了当前有效回文字符串内

那么 i 一定有一个关于当前有效回文字符串的中点即 mPoint 对称点, 我们称它为 j

作为同在当前有效回文字符串内的两个对称点, 在不超出当前有效回文字符串的左右两端的前提下, 注意这里要强调不超出, 我们不难想象, 如果以 为中点可以形成一个回文字符串, 那么一定也可以以 i 为中点形成一个相同的回文字符串(否则就不对称, 当前有效回文字符串就不可能存在).

于是我们可以整理下目前已经直接或者间接知道的事实:

(1) P[j]中已经存储了以 j 这个元素为中点可以形成的最长回文字符串半径

(2) 当前有效回文字符串是以mPoint为中心可以形成的最长回文字符串, 也就是说以mPoint为中心, 不可能再有更长的回文字符串, 并且其半径已经存储在了P[mPoint]

(3) 如果回文字符串内部对称位置上有两个回文子串, 那么这两个回文子串必然是相同的

进而我们可以有以下推导(请读者不妨拿出笔和纸画一画线段图, 帮助理解):

a. 当 P[j]>rPoint-i 时, 也就是说以 j 这个元素为中点形成的最长回文字符串足以延伸至当前有效回文字符串左端以外时, 则一定有P[i] = rPoint-i, 因为如果P[i] > rPoint-i, 说明以 i 这个元素为中点形成的最长回文字符串足以延伸至当前有效回文字符串右端以外, 那么当前有效回文字符串就应该有更长的半径, 但这与事实(2)矛盾了. 而如果P[i] < rPoint-i, 则在当前有效回文字符串内部就会出现不对称使其不能成为回文字符串, 这同样产生矛盾.

b. 当P[j]<rPoint-i时, 也就是说以 j 这个元素为中点形成的最长回文字符串完全被包裹在当前有效回文字符串内部时, 则一定有P[i] = P[j], 否则它与事实(3)矛盾

以上两种情况就可以直接得出P[i], 无需任何检查!

c. 当P[j] = rPoint-i时呢? 这时只能确定P[i]应至少等于rPoint-i, 否则会与事实(3)矛盾, 可是P[i]可以更大, 这时我们才需要令radius=rPoint-i开始检查(这就比令radius = 1开始检查高效)

以当前元素为中点, 半径为radius开始检查回文字符串, 如果是回文字符串则扩展半径radius, 尝试寻找更长的回文字符串, 直至失败

此时要满足两个条件:

②扩展半径之后, 左端和右端的下标不越界.

③当前左端和右端的元素字符相同.

如果条件②和③都成立,则P[i] = P[i] + 1

紧接着判断不等式④i+P[i] > rPoint

如果④成立, 说明当前回文字符串的右端已经延伸到了更靠右的位置, 即产生新的当前有效回文字符串

立即更新mPoint为当前的 i , rPointi+P[i]

mPoint=i

rPoint=i+P[i]

如果P[i]比已知的最大回文字符串半径都大, 记录并更新最大半径(根据最终需求结果不同也可能是记录mPointrPoint的下标), 

循环执行直至②或③不成立

第三步

自增, 重复执行第二步, 直至 到达字符数组的最有端(当然如果从 i 的位置不可能再找到比当前最长回文字符串更长的回文字符串时, 就可以提前结束)

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