回文串是这样的abba,ababa,就是把这个串翻转过来和原串是一样的, 最长回文子串,就是在一个长串中找到一个 子串,这个子串是 长串中的最长回文子串
简单的做法是 指定前后两个指针,判断这两个指针之间的字符串是否是回文串,并记录最大值
有一个算法是来计算最长回文子串的叫做Manacher,在网上找了半天有点没看懂,最后自己在纸上模拟一下才弄懂,说一下自己的思路
首先将字符串S用‘#’分隔,以"waawabawab" 为例子
原串S变成
S: # w # a # a # w # a # b # a # w # a # b #
然后我们用一个p数组,p[i]记录以str[i]为中间字符的回文串向右能匹配的长度 我们人工计算一下可以看到结果是这样的
S: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
i: # w # a # a # w # a # b # a # w # a # b #
p[i]: 1 2 1 2 5 2 1 4 1 2 1 8 1 2 1 6 1 2 1 2 1
可以看到 当i=4时,当前字符是‘#’,以它为中间字符能构成的最长回文串是#w#a(#)a#w# ,括号内为当前字符,所以结果是5,但是这个值用程序怎么做出呢,还要以它为中间字符向左右遍历查找吗,当然不是。。。
我们引入两个变量id和mx, mx记录的是能够影响到的最右边的位置, 并且mx是以S[id]为中间字符的
当位置是0时,id=0,mx=0
当位置是1时,id=1,mx=2
当位置是2时,id=1,mx=2
当位置是3时,id=3,mx=4
当位置是4时,id=4,mx=8
当位置是5时,id=4,mx=8
...
当位置是11时,id=11,mx=18
...
当位置是15时,id=11,mx=18
我们就以这个位置为15的字符w为例子,假设前面的p[i]都已经算出来了,该怎么算出这个位置的p值呢,可以看到当前字符是在 以S[id]为中间字符的最长回文串里,那么以id=11做一个对称,位置为7的也应该w
而且p[15] 应该 等于 p[7]。。。7是这样算出来的,当前位置是i,以id做对称,那么位置应该是 2*id - i
这里有一个问题,我们先让p[i] = p[2*id-i] ,那么如果 以S[2*id-1]为中间字符的最长回文子串 最左端 已经超过了 以以S[id]为中间字符的最长回文子串最左端, 这就出现了问题,所以我们应该让
p[i] = min(p[2*id-1],mx-i+1); // mx-i+1 是能影响到的最右端的位置
然后我们在向后遍历
while(i-p[i]>=0&&S[i+p[i]] == S[i-p[i]]) {
p[i]++;
}
这样就能得到正确的结果了,并且需要更新id和mx值
代码是leetcode 第五题
char* longestPalindrome(char* s) {
int lens = strlen(s);
char as[100000];
int p[100000];
int i=0,j=0;
// add # into s
as[j++] = '#';
for(i=0; s[i]; i++){
as[j++]=s[i];
as[j++]='#';
}
as[j] = 0;
int id,mx=0;
int result=0,resultId;
p[0]=1;
for(i=1; as[i]; i++){
p[i] = 1;
if(mx > i){
p[i] = p[2*id-i];
if(p[i] > mx-i+1) p[i] = mx-i+1;
}
while(i-p[i]>=0&&as[i+p[i]] == as[i-p[i]]) {
p[i]++;
}
if(i+p[i]-1 > mx) {
mx = i+p[i]-1;
id = i;
}
if(p[i]>result) {
result = p[i];
resultId = i;
}
}
int left = (resultId - result +1);
int right = (resultId + result -1);
if(as[left]=='#') left++;
if(as[right]=='#') right--;
left/=2; right/=2;
s[right+1]=0;
return s+left;
}
来源:CSDN
作者:曾经最动心
链接:https://blog.csdn.net/niechangxu/article/details/48154251