说明:转载请注明出处
题目:
给定一个字符串,求它的最长回文子串的长度。
思路一:采用中间法比较
/*说明:中心法求最长回文子串
时间复杂度为O(n^2)时间,空间复杂度O(1)
思路:如果一段字符串是回文,那么以某个字符为中心的前缀和后缀都是相同的
反之则肯定不是回文字符串,不过要注意区分奇偶
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*以low和high为中心找到最长回文字符串长度*/
int centerPalindrome(char*S, int N, int low, int high)
{
int max = 0;
/*需要单独处理,返回正确长度*/
if (low == high)
{
max = 1;
low--;
high++;
}
while (low>=0 && high<N)
{
if (S[low] != S[high])
{
break;
}
low--;
high++;
max += 2;
}
return (max==0)?1:max;
}
/*主程序,通过中间开始判断获取最长回文字符串*/
char* longestPalindrome(char* S, int N)
{
int i;
int j = 0;
int mid = 0;
int maxLen = 0;
int max = 0;
char *str = NULL;
int start;
int end;
for (i=0; *(S+i)!='\0'; i++)
{
/*奇序列*/
max = centerPalindrome(S, N, i, i);
if(maxLen < max)
{
maxLen = max;
mid = i;
}
/*偶序列*/
max = centerPalindrome(S, N, i, i+1);
if(maxLen < max)
{
maxLen = max;
mid = i;
}
}
/*分配内存将最大回文字符串返回*/
str = (char *)malloc(maxLen+1);
start = mid-(maxLen-1)/2;
end = start + maxLen;
for (i=start; i<end; i++)
{
str[i-start] = *(S+i);
}
str[maxLen] = '\0';
return str;
}
int main()
{
char s[] = "123321";
printf("%s\n", longestPalindrome(s, strlen(s)));
return 0;
}
思路二:采用动态规划求解
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/*采用动态规划求解,考虑要求最大回文字符串,如
"aba"已经是回文字符串,则"xabax"必然也是回文字符串
那思路有了,实现考虑用数组记录任意i到j是否为回文字符串
table[i][i]肯定为1,那table[i][j]=table[i+1][j-1] && s[i]=s[j]
即如果从i到j是回文则s[i]和s[j]必然应该相等,且从i+1到j-1必然是回文
*/
char* longestPalindrome(char* S, int N)
{
int i;
int j;
int mid = 0;
int maxLen = 1;
int len;
bool table[100][100] = {false};
char* str = NULL;
/*初始化table[i][i]为TRUE*/
for (i=0; i<N; i++)
{
table[i][i] = true;
/*初始化table[i][i+1]*/
if (i<N-1 && S[i]==S[i+1])
{
if (maxLen == 0)
{
maxLen = 2;
mid = i;
}
table[i][i+1] = true;
}
}
for (len=2; len<=N; len++) /*回文字符串总长度*/
{
for (i=0; i<N-len+1; i++) /*回文字符串开始的位置*/
{
j = i+len-1; /*回文字符串结束的位置*/
if (S[i]==S[j] && table[i+1][j-1])
{
table[i][j] = true;
if (maxLen < len)
{
maxLen = len;
mid = i;
}
}
}
}
str = (char*)malloc(maxLen+1);
j = 0;
for (i=mid; i<mid+maxLen; i++)
{
str[j++] = *(S+i);
}
str[j] = '\0';
return str;
}
int main()
{
char s[] = "012332";
char* str = longestPalindrome(s, strlen(s));
printf("str %s\n", str);
free(str);
str = NULL;
return 0;
}
思路三:Manacher算法,且这个算法求最长回文子串的时间复杂度是线性O(N)的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MIN(X, Y) ((X)<(Y)?(X):(Y))
/*说明:manacher可以将找最大回文字符串的时间复杂度降低为O(n)
具体文章可以参考(我觉得比较好理解的文章):
http://blog.csdn.net/runninghui/article/details/39322649
http://blog.csdn.net/synapse7/article/details/18908413
*/
/*将字符串S加工,如S="abc", 则T="$#a#b#c#"*/
char* preProcess(char *S, int N)
{
char* T = NULL;
int i;
int j = 0;
T = (char*)malloc((N+1)*2);
/*注意:不能直接用T++往前移动,返回的时候T已经指向字符串末端,这样有问题*/
T[j++] = '$';
T[j++] = '#';
for (i=0; *(S+i)!='\0'; i++)
{
T[j++] = *(S+i);
T[j++] = '#';
}
T[j] = '\0';
return T;
}
/*找最大回文字符串主程序*/
char* manacher(char* S, int N)
{
int id = 0;/*记录i之前最大回文子串中心的位置*/
int *p = NULL; /*P[i]记录以字符S[i]为中心的最长回文子串长度*/
int mx = 0;/*记录id+p[i]的值*/
char *str = NULL;
char *T;
int i;
int mid = 0;
int maxLen = 0;
int start;
int end;
T = preProcess(S, N);
p = (int *)malloc(sizeof(int)*(strlen(T)));
memset(p, 0, sizeof(int)*(strlen(T)));
for (i=1; *(T+i)!='\0'; i++)
{
/*如果mx比i大,则p[i]至少比mx-i或者p[j]要大,其中j为i关于id的对称点*/
if (mx > i)
{
p[i] = MIN(mx-i, p[2*id-i]);
}
/*否则认为没有覆盖到i值,为默认值*/
else
{
p[i] = 1;
}
/*以i为中心往两边扩展,更新p[i]的值*/
while (*(T+i-p[i]) == *(T+i+p[i]))
{
p[i]++;
}
/*如果以i为中心的回文字串更大则更新id的值*/
if (i+p[i] > mx)
{
id = i;
mx = i+p[i];
}
/*注意此处需要重新比较,上面比较和此处有区别*/
if (p[i] > maxLen)
{
mid = i;
maxLen = p[i];
}
}
str = (char *)malloc(maxLen);
start = (mid-maxLen)/2;
end = start+maxLen;
for (i=start; i<end; i++)
{
str[i-start] = *(S+i);
}
str[maxLen-1] = '\0';
free(T);
T = NULL;
return str;
}
int main()
{
char s[] = "132321abc";
char *t = manacher(s, strlen(s));
printf("result %s\n", t);
free(t);
t = NULL;
}