回文和回文子串
回文串:顺着读和倒着读都一样的字符串;
回文子串:给定字符串string,若str同时满足以下两个条件:1)str是string的子串;2)str是回文串;那么str就是string的回文子串;
引出问题
要求求出上面string中最长的那个回文子串;
解决方案
方案一:枚举中心位置,对奇数位串和偶数位串分开处理;
int AllAlgorithms::longestPalindrome(const char *s, int n){
int max,j;
if (0 == s || n < 1) {
return 0;
}
max = 0;
for (int i = 0; i < n ; ++ i) {//i为回文串的中心
for ( j = 0; ((i - j) >= 0) && ((i+j) < n); ++j) {//如果回文串的长度是奇数
if (s[i-j] != s[i+j]) {
break;
}
if ((j * 2 + 1) > max) {
max = 2*j + 1;
}
}
for (j = 0; ((i-j) >= 0) && ((i+j+1) < n); ++j) {//如果回文串的长度是偶数
if (s[i-j] != s[i+j+1]) {
break;
}
if ((j * 2 + 2) > max) {
max = 2*j + 2;
}
}
}
return max;
}
该方案比较耗时累赘,因为在判断一个串是否是回文串时,需要考虑奇数和偶数位的而不同,要分开处理,就造成了代码的大量冗余,因此该方法不可取;
方案二:Manacher算法
对于一个长度为n的字符串,考虑如下问题:
长度为n的字符串,一定有n-1个“邻接间隙”,在加上首字符的前面,以及尾部字符的后面,一共有n-1+2=n+1个“gap”,然后再加上字符串本身,一共有字符n+n+1=2n+1个,这样一扩展,可以发现,它一定为奇数,因此这样做了扩展后,我们可以只考虑奇数的情况,无需考虑偶数的情况,大大简化了代码;
下面我们就来介绍有关Manacher算法的解决思路是什么样的:
介绍之前先给出两个trick:
1)为处理一致,在字符串最前面加上一位未出现过的字符,如$:
2)用一个数组P[i]来记录以字符S[i]为中心的最长回文子串向左或向右扩张的长度(包括S[i]),如:
算法的任务:在P[0...i-1]已知的前提下,计算P[i]的值,换句话说就是,在P[0...i-1]已知的前提下,能否为P[i]的计算提供有用的信息;
算法核心:
1)简单遍历,得到i个三元组{k,P[k],k+P[k]},0≤k≤i-1;以k为中心的字符形成的最大回文子串的最右位置是k+P[k]-1;
2)以k+P[k]为关键字,找出上述i各三元组中,k+P[k]最大的那个三元组,记作(id,P[id],id+P[id]),为了简化起见,进一步记mx=id+P[id],于是得到三元组为(id,P[id],mx),含义就是:在上述遍历得到的所有三元组中,向右到达最远的位置,就是mx;
3)在计算P[i]的时候,考查i是否落在了区间[0,mx)中:若i落在区间[0,mx)的右侧,说明区间[0,mx)没能控制住i,P[0...i-1]已知的前提下不能为P[i]的计算提供有用的信息;若i落在mx的左侧,则说明[0,mx)控制住了i,可以提供一些有用的信息;
下面我们用图示来看一下Manacher算法的递推关系:
1)
记i关于id的对称点为j,j=2*id-i,如果此时满足:mx-i >P[j],则:
记my为mx关于id的对称点:my = 2*id-mx;
因为以S[id]为中心的最大回文子串为:S[my+1...id...mx-1],而S[my+1...id]与S[id...mx-1]对称,i和j也关于id对称,所以P[i]=P[j];
2)
上图中,同样的:
记i关于id的对称点为j,j=2*id-i,如果此时满足:mx-i < P[j],则:
记my为mx关于id的对称点:my = 2*id-mx;
因为以S[id]为中心的最大回文子串为:S[my+1...id...mx-1],而S[my+1...id]与S[id...mx-1]对称,i和j也关于id对称,所以P[i]至少是等于上图中绿色框部分,即等于mx-i;
void AllAlgorithms::Manacher(char *s, int *p){
int size = (int)strlen(s);
p[0] = 1;
int id = 0,mx = 1;
for (int i = 1; i < size; i ++) {
if (mx > i) {
p[i] = min(p[2*id - i], mx- i);
}
else
p[i] = 1;
for (; s[i+p[i]] == s[i- p[i]]; p[i] ++);
if (mx < i + p[i]) {
mx = i + p[i];
id = i;
}
}
}
int AllAlgorithms::min(int a, int b){
return ((a+b) - abs(a-b))/2;
}
来源:CSDN
作者:eternity1118_
链接:https://blog.csdn.net/eternity1118_/article/details/52068020