马拉车算法(Manacher Algorithm)--用于计算最长回文子串

马拉车算法的目标是找到一串字符串中的最长回文子串,优点是时间复杂度为O(n)
现以寻找 “cgbaabgk” 中的最长子回文串( “gbaabg”)为例进行说明

算法主要过程(总共3步):

1.改造字符串结构:

字符坐标 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
char $ # c # g # b # a # a # b # g # k #

将原字符串"c g b a a b g k"改造成 "$ # c # g # b # a # a # b # g # k # "
改造的原则是,先在每个字母之间插入’#‘号,再在最前面加入’$'符号
(先不用管为什么这么改造,后面再说明)


2.计算新字符串中,以每个字符为中心的子回文串半径:
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第1张图片

例如:
‘$’ 为中心能创建的回文串就是 " $ “,这个字符串内就只有它一个字符,那么回文串半径是1;
以坐标4号 ‘g’ 为中心能创建的回文串是”#g#",长度是3,所以以字符’g’为中心的回文串半径为2
坐标为7的’#'号 为中心能创建的最长回文串为"#g#b#a#a#b#g#",长度为13,回文串的半径是"#a#b#g#",长度是7;
这里的回文串半径不是单纯的回文串长度除以2,而是从回文串中心到回文串最右边的字符的长度。


3.通过步骤2的表格获得我们想要的数据:
我们改造字符串、计算以每个字符为中心的最长回文串半径,都是为了方便后面得到我们想要的结果。
例如现在,如果我想知道原来的整个字符串 “cgbaabgk” 中,最长的回文串的长度是多少,那就是表格中radius一栏的最大数减一:
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第2张图片
即7-1=6,这个字符串里面的最长回文串长度为6。

如果我想求出这个字符串里面的最长回文子串,可以得到这个回文串在原字符串中的起点和终点坐标:
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第3张图片
最长子回文串为"baab":
起点字符’b’在原字符串中的坐标:(9-radius[9])/2=(9-7)/2=1
终点字符’b’在原字符串中的坐标:(9+radius[9]-1)/2-1=(7+5-2)/2-1=4
通过这两个坐标可以得到原字符串中的最长回文子串"baab"


马拉车算法就只有这三步,那么如何高效地获得这个radius一栏的值呢?
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第4张图片

如何获得改造后的字符串的回文半径:

同样以上面的字符串“$#c#g#b#a#a#b#g#k#”为例来说明,马拉车算法是从左到右,依次计算各字符的回文半径。但是这里计算不是蛮力法一个一个去算,而是有技巧的。


这里有三种情况需要讨论:
1.当我们已经从左到右计算到9号’#'号的回文半径为7时:

马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第5张图片
由对称性可知,方框1和方框2中的内容是关于9号’#'字符对称的,所以10号’a’是关于8号’a’对称的6号’a’的回文半径是2,故8号’a’的回文半径也是2。所以我们可以直接认为8号’a’的回文半径就是2了,而不用继续蛮力求它的回文半径长度。

2.当我们想求14号’g’的回文半径时,是否也能直接认为它的回文半径是4号’g’的回文半径2呢?不能。因为可以看到,坐标4的’g’的势力范围已经接触到9号’#'的最大势力范围了,我们没有办法保证14号’g’是否会有更大的回文半径
比如这里如果15号如果不是’k’而是’a’的话,那’g’的回文半径就不是2,是4(回文字符串"#a#b#a#"的回文半径是4)。
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第6张图片

所以我们先假设14号’g’的回文半径是2,,然后继续用蛮力法探究一下14号’g’的回文半径到底能扩散到哪里。
通过继续蛮力比较可以看到以14号坐标’g’为中心的的回文串为"#b#",故回文半径为2.
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第7张图片

然后开始计算坐标15’#‘的回文半径:
此时15号’#'已经在9号‘#’的势力范围之外了,所以不能再借助9号’#‘的回文半径来估算15号’#‘的回文半径了,只能老老实实的蛮力算。
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第8张图片
可以算出以15号’#‘为中心的回文串为"#"(即就它一个),所以11号’#'的回文半径是1.


这样一个一个算下去就可以得到以每个字符为中心的回文半径:
马拉车算法(Manacher Algorithm)--用于计算最长回文子串_第9张图片

得到一行回文半径后,根据上述第三步的内容就可以得到我们想要的结果(最长半径是多少、最长回文子串是什么)

你可能感兴趣的:(算法)