问题
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例2:
输入: "cbbd"
输出: "bb"
分析
回文是一个正读和反读都相同的字符串,例如,“aba” 是回文,而 “abc” 不是,“ab”也不是,但“a”是。
通常来讲,有多种方法可以解决回文问题。
-
最长公共子串
- 核心思想是,反转s,得到s1,找到s和s1之间最长的公共子串,且子串的索引与反向子串的原始索引相同,则返回该公共子串。
- 它是复杂度是O(n^2), 空间复杂度是O(n^2)。
-
暴力法
- 思想:找出所有子字符串可能的开始和结束位置,并检验它是不是回文。
- 时间复杂度O(n^3),空间复杂度O(1)。
-
动态规划
- 思想:对暴力法改进,避免在验证回文时的重复计算。
- 时间复杂度O(n2),空间复杂度O(n2)。
-
中心扩展法
- 思想:回文中心的两侧互为镜像,所以回文可以从它的中心展开,并且只有(2n-1)个这样的中心。
- 时间复杂度O(n^2),空间复杂度O(1)。
-
Manacher 算法
- 思想:通过巧妙的构思,把时间复杂度降低到O(n)。
代码
c代码:
static int max(int a, int b)
{
if (a > b) {
return a;
}
return b;
}
static int expand_center(char *s, int left, int right)
{
while (left >= 0 && right < strlen(s) && s[left] == s[right]) {
left--;
right++;
}
return right - left - 1;
}
char* longestPalindrome(char* s) {/* 中心扩展法 */
int len, len1, len2, len_max;
int start = 0, end = 0;
int i;
char *res = NULL;
if (!s) {
printf("invalid para\n");
return NULL;
}
len = strlen(s);
for (i = 0; i < len; i++) {
len1 = expand_center(s, i, i);
len2 = expand_center(s, i, i + 1);
len_max = max(len1, len2);
if (len_max > end - start) {
start = i - (len_max - 1) / 2;
end = i + len_max / 2;
}
}
res = calloc(1, 1001);
if (!res) {
printf("calloc error: %m\n");
return NULL;
}
memcpy(res, s + start, end - start + 1);
return res;
}