最长回文子串与Manacher算法

关注
最长回文子串与Manacher算法www.shan-machinery.com

题目描述给定一个字符串,求它的最长回文子串的长度。

最简单粗暴的方法就是,枚举全部的字符串,然后每个都判断一下是不是回文,然后得到长度最长的字符串。显然,这个方法是可行的,可是也是效率极其低下的。

聪明一点的办法是枚举以每个字符作为中心,然后向两边扩展的字符串

例如字符串abcba:

    以a为中心扩展,则最大回文长度为1

    以b为中心扩展,因为a!=c,所以,最大回文长度也是1

    以c为中心,有b==b,a==a,最大回文长度是5

    ......

    得到最大回文长度是5

int LongestPalindrome(const char * s, int n) {  int i, j, max, c;  if (s == NULL || n = 0) && (i + j + 1  max)      max = c;  }  return max;}

    由于奇数和偶数中心的问题如,abba,这是一个偶数的回文字符串,所以b都是中心位置;abcba,这则是一个奇数的回文字符串,中心为c。

    这个算法有两层for循环,那么,有没有更优的方法呢?答案是肯定的。

    Manacher算法提供了一种很巧妙的方法

    

    The-Art-Of-Programming-By-July书上讲的很清楚:

    首先通过在每个字符的两边都插入一个特殊的符号,将所有可能的奇数或偶数长度的回文子串都转换成了奇数长度。比如 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[i]-1正好是原字符串中最长回文串的总长度,为5。

    接下来怎么计算P[i]呢?Manacher算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。得到一个很重要的结论:- 如果mx > i,那么P[i] >= Min(P[2 * id - i], mx - i)

——《The-Art-Of-Programming-By-July》

    其实,简单来说就是,j是当前下边i关于id(当前所能到达最右端的中心下标)的对称点,因为是mx是能到达最右端的下标,所以mx-i > 对称点的最大回文长度时候,以当前下标为中心的回文长度应该大于等于对称点的回文长度(因为回文也是对称的);当mx-i mx) {      mx = i + p[i];      id = i;    }    ret = max(ret, p[i]);  }    return ret-1;}

    引用:The-Art-Of-Programming-By-July

https://www.shan-machinery.com