5. 最长回文子串

342 阅读2分钟

题目描述

给你一个字符串s,找到s中最长的回文子串。

示例

示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:
输入:s = "cbbd"
输出:"bb"

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/lo…

实现

  1. 基本思想:遍历字符串s,以其中的每一个字符为中心向两边发散(左右指针),如果两边的相等,则组成回文,直到遇到第一个不相等的为止。
  2. 由于字符串有奇数、有偶数,分开处理较为繁琐,因此在每个字符两边增加#标记,这样不论字符个数是奇数还是偶数,最后形成的字符串都是奇数。如"aba"变为"#a#b#a#,"ab"变为"#a#b#"。
  3. 以字符串"cbbd"为例,我们先将字符串加标记形成新的字符串"#c#b#b#d#",然后开始做如下遍历:
  • #: 此时"#"自己单独形成长度为1的回文,左指针向左走到-1,右指针向右走到1位置,由于左指针走到-1,越界,不符合回文特征,因此以0位置的"#"为中心形成的最大回文字符串长度为1;
  • c: 此时"c"自己单独形成长度为1的回文,左指针向左走到0位置的"#",右指针向右走到2位置的"#",相等,此时最大回文长度为3("#c#");
  • ...
  • d: 此时"d"自己单独形成长度为1的回文,左指针向左走到6位置的"#",右指针向右走到8位置的"#",此时最大回文长度为3("#d#"),左指针继续向左走到5位置的"b",右指针向右走到9位置越界,不符合回文特征,因此以7位置的"d"为中心形成的最大回文字符串长度为3;
  • ...
// 以字符为中心向左右扩展
void GetExpendString(char *s, int len, int value, int *begin, int *end)
{
    int left = value;
    int right = value;
    while (left >= 0 && right < len && s[left] == s[right]) {
        left--;
        right++;
    }
    *begin = left + 1;
    *end = right - 1;
}

//以数组中的每个字符为中心向两边扩展,查看最长回文子串,由于字符串可能是单数或者双数,
//所以需要分开判断,不方便,因此将字符串都加上#变为单数,如abba->#a#b#b#a#
char * longestPalindrome(char * s)
{
    int i = 0;
    int flag = 0;       // 用于去除添加的#
    int begin = 0;      // begin和end用于返回每一个字符可以组成的回文范围
    int end = 0;
    int resBegin = 0;   // 保存最大回文的头和尾
    int resEnd = 0;
    int len = 2 * strlen(s) + 1;  // 加#后构建的字符串的长度
    if (s == NULL) {
        return NULL;
    }
    if (strlen(s) == 1) {
        return s;
    }
    
    // 构造新字符串,要作为结果返回,所以使用malloc分配到堆中
    char *res = (char*)malloc(sizeof(char) * (2 * strlen(s) + 1));    // 加上#之后的字符串
    for (i = 0; i < 2 * strlen(s) + 1; i++) {
        if (i % 2 == 0) {
            res[i] = '#';
        } else {
            res[i] = s[flag++];
        }
    }

    // 对新字符串的每一个字符进行处理,每个字符串前后走,看是否也相等
    for (i = 0; i < 2 * strlen(s) + 1; i++) {
        GetExpendString(res, len, i, &begin, &end);
        if (end - begin > resEnd - resBegin) {
            resBegin = begin;
            resEnd = end;
        }
    }
    flag = 0;
    // 去除添加的#字符串,返回结果
    for (i = resBegin; i <= resEnd; i++) {
        if (res[i] != '#') {
            res[flag++] = res[i];
        } 
    }
    res[flag] = '\0';
    return res;
}