题目描述
给定一个字符串,求它的最长回文子串的长度。
最简单粗暴的方法就是,枚举全部的字符串,然后每个都判断一下是不是回文,然后得到长度最长的字符串。显然,这个方法是可行的,可是也是效率极其低下的。
聪明一点的办法是枚举以每个字符作为中心,然后向两边扩展的字符串
例如字符串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 < 1) {
return 0;
}
max = 0;
for(i = 0; i < n; i++) {
for(j = 0; (i - j >= 0) && (i+j < n); j++) {
if (s[i - j] != s[i + j])
break;
c = j * 2 + 1;
}
if (c > max)
max = c;
for(j = 0; (i - j >= 0) && (i + j + 1 < n); j++) {
if (s[i - j] != s[i + j + 1])
break;
c = j * 2 + 2;
}
if (c > 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是以当前下标为中心的回文长度的最小值。
这个算法的优点在于,它不仅解决了奇偶数的讨论问题,还记录了当前字符串的“回文状态”,利用回文来求回文 ,保存了枚举的信息,使得算法能够在线性时间内完成检索任务。
int Manacher(char * str, int n) {
char s[2004];
int i, j;
int t = 0;
int ret = 1;
int p[2004], mx = 0, id = 0;
memset(p, 0, sizeof(p));
s[0] = '$';
for(i = 1; i < 2 * n + 2; i++) {
if(i & 1) {
s[i] = '#';
} else {
s[i] = str[t++];
}
}
s[i] = '@';
s[i+1] = '\0';
for (i = 1; s[i] != '\0'; i++) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while(s[i+p[i]] == s[i-p[i]]) {
p[i]++;
}
if (i + p[i] > mx) {
mx = i + p[i];
id = i;
}
ret = max(ret, p[i]);
}
return ret-1;
}
引用:The-Art-Of-Programming-By-July
来源:oschina
链接:https://my.oschina.net/u/1585804/blog/415675