最长回文子串——Manacher算法

1.对于最长回文字串问题,最简洁暴力的方式就是:找到所有的子串,判断是否为回文子串,记录回文子串长度,选最长的。但是这种解法的时间复杂度是 O(n3) O ( n 3 )
2.Manacher算法
对于所有的回文子串,都有同样的性质,那就是对称,长度为奇数的回文子串,以中间字符为对称轴左右对称,长度为偶数的回文子串,对称轴是中间俩字符的空隙。Manacher算法之所以能提高效率,最主要的原因就是利用了这个对称性。
(1)解决长度是奇偶性带来的对称性问题
首先将字符串进行预处理

aab:#a#b#a#
aabab:#a#a#b#a#b

(2)解决重复访问的问题
创建一个新的数组RL,用RL[i]表示第i个字符为对称轴的回文串的回文半径,这里定义RL[i]为第i个字符为对称轴的回文串的最右一个字符与字符i的距离。

char:# a # b # a #
RL  :1 2 1 4 1 2 1
RL-1:0 1 0 3 0 1 0
i   :0 1 2 3 4 5 6

RL-1即为没插入#时,位置i对应的回文子串的长度。
接下来,该怎么求RL[i]是个问题了。Manacher的基本思路是利用了回文串的对称性。
这里引用一个辅助变量maxRight用来记录当前访问到的回文子串的最右的位置,引用变量pos记录maxRight对应的回文串的中心位置。
最长回文子串——Manacher算法_第1张图片
当i在maxRight的左边,又会分为两种情况:与i对称的位置j,j对应的R[j]很短;j对应的R[j]很长
最长回文子串——Manacher算法_第2张图片
当R[j]很短时,则可以令R[i]=R[j],以i为对称轴,向两边延伸,改变pos和maxRight的值。
最长回文子串——Manacher算法_第3张图片
当R[j]很长时,则回文子串的半径长度为maxRight-i,可以从这个长度开始向两边延伸。具体操作如下:

R[i] = min(R[2*pos-i], maxRight-i)
以i为中心扩展,直到两边字符不相同,或者到达边界
更新maxRight和pos

当i在maxRight右边时,则说明i为对称轴的回文串还没有任何一部分被访问过,,只能从i左右两边开始扩展,然后更新maxRight和pos

算法实现(C++):

int pos = 0;
int maxRight = 0;
for(i = 0; s[i] != \0'; ++i)
{
    RL[i] = maxRight > i ? min(RL[2*pos-i], maxRight-i) : 1;
    while(s[i+RL[i]] == s[i-RL[i]])
        RL[i]++;
    if (i+RL[i] > maxRight)
    {
        maxRight = i + RL[i];
        pos = i;
    }
}

参考:https://segmentfault.com/a/1190000003914228

你可能感兴趣的:(Leetcode)