最长回文子串
![](http://dzliving.com/%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.jpg)
1、问题描述
给定一个字符串 s,找到 s 中最长的回文子串。
2、算法分析
大多数问题都可以用暴力法解决。暴力法将选出所有子字符串可能的开始和结束位置,并检验它是不是回文。
时间复杂度:O(n3),假设 n 是输入字符串的长度,则 (n * (n−1))/2 为此类子字符串(不包括字符本身是回文的一般解法)的总数。因为验证每个子字符串需要 O(n) 的时间,所以运行时间复杂度是 O(n3)。
空间复杂度:O(1)。
为了改进暴力法,我们首先观察如何避免在验证回文时进行不必要的重复计算。考虑 "ababa" 这个示例。如果我们已经知道 "bab" 是回文,那么很明显,"ababa" 一定是回文,因为它的左首字母和右尾字母是相同的。
我们给出 P(i,j) 的定义如下:
如果子串 Si...Sj 是回文子串 p(i, j) = true
其他情况 p(i, j) = false
因此:
P(i, j) = ( P(i+1, j-1) and Si == Sj)
基本示例如下:
P(i, i) = true
P(i, i+1) = (Si == Si+1)
这产生了一个直观的动态规划解法,我们首先初始化一字母和二字母的回文,然后找到所有三字母回文,并依此类推…
3、复杂度分析
时间复杂度:O(n2),这里给出我们的运行时间复杂度为 O(n2)。
空间复杂度:O(n2),该方法使用 O(n2) 的空间来存储表。
4、代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * longestPalindrome(char* s)
{
if (s == NULL || strlen(s) < 1) {
return "";
}
int n = (int)strlen(s);
// 初始化二维数组
int matrix[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j< n; j++)
matrix[i][j] = 0;
int left = 0;
int right = 0;
// i 从倒数第二个开始,从右往左。标识子串的起始位置
for (int i = n - 2; i >= 0; i--) {
matrix[i][i] = 1;
// j 从 i 的下一位开始,从左往右。标识子串的结束位置
for (int j = i + 1; j < n; j++) {
// ①、s[i] == s[j] ②、小于或等于 3 个字符 ③、内缩一位是回文子串
matrix[i][j] = (s[i] == s[j] && (j - i < 3 || matrix[i+1][j-1]));
// 获得最长的子串位置
if(matrix[i][j] && right - left < j - i) {
left = i;
right = j;
}
for (int m = 0; m < n; m++) {
for (int k = 0; k < n; k++)
printf("%d ", matrix[m][k]);
printf("\n\n");
}
}
}
char* sub = (char *)malloc(sizeof(char) * (right - left + 1));
int i = 0;
for (; i < right - left + 1; i++) {
sub[i] = s[left + i]; //strncpy(sub, s+left, right+1);
}
sub[i] = '\0';
return sub;
}
int main()
{
printf("%s", longestPalindrome("aaabaaaa"));
return 0;
}
关键代码:
matrix[i][j] = (s[i] == s[j] && (j - i < 3 || matrix[i+1][j-1])); 以及 i 和 j 的取值方向