Leetcode第5题: 最长回文子串

一、题目来源:leetcode第5题

二、题目描述

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

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

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

示例 2:

输入: s = "cbbd" 输出: "bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

三、解题

3.1 解法一

  1. 思路

    定义两个指针,使用中心扩展法来找到回文子串。遍历字符串中的每个字符,以当前字符为中心向两边扩展,分别检查奇数长度和偶数长度的回文子串。

  1. 解法

/**
 * 在给定的字符串中找到最长的回文子串。
 *
 * @param {string} s - 输入字符串。
 * @return {string} - 找到的最长回文子串。
 */
const longestPalindrome = function(s) {
  let res = '';

  for (let i = 0; i < s.length; i++) {
    const odd = expandAroundCenter(s, i, i);
    const even = expandAroundCenter(s, i, i + 1);
    const longest = odd.length > even.length ? odd : even;
    if (longest.length > res.length) {
      res = longest;
    }
  }
  return res;

  /**
   * 在给定的字符串中找到最长的回文子串。
   *
   * @param {string} s - 输入字符串。
   * @param {number} l - 要检查的子串的左索引。
   * @param {number} r - 要检查的子串的右索引。
   * @return {string} - 找到的最长回文子串。
 */
  function expandAroundCenter(s, l, r) {
    while (l >= 0 && r < s.length && s[l] === s[r]) {
      l--;
      r++;
    }
    return s.substring(l + 1, r);
  }
};
  1. 时间复杂度

由于遍历字符串需要 O(n) 的时间,而在每个位置上进行中心扩展需要 O(n) 的时间,因此总体时间复杂度为 O(n^2)

  1. 空间复杂度

算法只使用了常数个变量来保存中间结果,没有使用额外的数据结构或数组。因此,空间复杂度可以表示为O(1)

3.2 解法二

  1. 思路

马拉车算法(Manacher's Algorithm),基本思想是利用回文串的对称性来减少不必要的重复计算。它通过维护一个回文半径数组,以及一个当前已知的最右回文边界,来快速计算每个字符位置的回文半径。该算法的具体实现比较复杂,但可以在线性时间内找到最长回文子串。

  1. 解法

/**
 * 在给定的字符串中找到最长的回文子串。
 *
 * @param {string} s - 输入字符串。
 * @return {string} - 找到的最长回文子串。
 */
const longestPalindrome = function(s) {
  // 预处理字符串,在每个字符之间插入特殊字符
  const preprocessString = function(s) {
    let result = '^';
    for (let i = 0; i < s.length; i++) {
      result += '#' + s.charAt(i);
    }
    result += '#$';
    return result;
  };
  // 预处理字符串
  const processedString = preprocessString(s);
  const n = processedString.length;
  const palindromeRadius = new Array(n).fill(0); // 回文半径数组
  let center = 0; // 当前已知的最右边回文子串的中心
  let right = 0; // 当前已知的最右边回文子串的右边界

  for (let i = 1; i < n - 1; i++) {
    // 利用对称性得到一部分回文信息
    if (i < right) {
      const mirror = 2 * center - i;
      palindromeRadius[i] = Math.min(right - i, palindromeRadius[mirror]);
    }

    // 中心扩展法检查以当前字符为中心的回文子串
    while (
      processedString[i + 1 + palindromeRadius[i]] ===
      processedString[i - 1 - palindromeRadius[i]]
    ) {
      palindromeRadius[i]++;
    }

    // 更新最右边回文子串的中心和右边界
    if (i + palindromeRadius[i] > right) {
      center = i;
      right = i + palindromeRadius[i];
    }
  }

  // 找到最长回文子串的中心位置和长度
  let maxRadius = 0;
  let centerIndex = 0;
  for (let i = 1; i < n - 1; i++) {
    if (palindromeRadius[i] > maxRadius) {
      maxRadius = palindromeRadius[i];
      centerIndex = i;
    }
  }

  // 根据中心位置和长度获取原始字符串中的最长回文子串
  const start = (centerIndex - maxRadius) / 2;
  const end = start + maxRadius;
  return s.substring(start, end);
};
  1. 时间复杂度

    1. 预处理字符串的时间复杂度为 O(n),其中 n 是字符串的长度。在预处理过程中,我们在每个字符之间插入特殊字符(通常是#),以处理偶数长度的回文子串。

    2. 马拉车算法的主要部分是一个线性的循环,遍历预处理后的字符串。在该循环中,我们使用对称性和中心扩展法来计算每个字符作为回文子串中心时的回文半径。

      1. 对称性的计算时间复杂度为 O(n)。我们维护两个变量 centerright,并利用已知回文子串的对称性来更新回文半径。
      2. 中心扩展法的时间复杂度为 O(n)。我们在每个字符为中心时,向左右两侧扩展并检查回文性质。
    3. 因此,整个算法的时间复杂度为 O(n)

  1. 空间复杂度

    1. 预处理后的字符串需要 O(n) 的额外空间,因为我们在每个字符之间插入特殊字符。
    2. 回文半径数组需要 O(n) 的额外空间,用于存储每个字符作为回文子串中心时的回文半径。
    3. 因此,总体空间复杂度为 O(n)

四、知识点

双指针算法是一种常用的技巧,在解决多种问题时都可以应用。双指针的使用方式分以下3种:

  1. 快慢指针:快慢指针是一种特殊的双指针技巧,其中一个指针(快指针)移动的速度比另一个指针(慢指针)快。这种技巧常用于链表相关的问题,例如判断链表是否有环、找到链表的中间节点等。快慢指针可以根据问题的要求灵活调整速度和距离。
  2. 同向 双指针:同向双指针是指两个指针都向同一个方向移动。通常,两个指针的起始位置相同,然后逐步向右或向左移动。这种双指针移动方式常用于数组或字符串的搜索问题,例如在有序数组中查找两个数的和、找到满足特定条件的子数组等。
  3. 相向 双指针:相向双指针是指两个指针分别从两个不同的方向移动,通常是从数组或字符串的两端开始。这种双指针移动方式常用于有序数组或字符串的搜索问题,例如找到两个有序数组的公共元素、判断一个字符串是否为回文串等。相向双指针可以逐步缩小搜索范围,从而提高算法的效率。

双指针算法适用于解决许多问题,包括但不限于以下情况:

  1. 数组或字符串的搜索:当要在数组或字符串中查找特定元素或满足特定条件的子数组/子串时,可以使用双指针算法。例如,有序数组的两数之和、三数之和等问题。
  2. 数组或字符串的反转或交换:使用双指针可以实现对数组或字符串的反转或交换操作。例如,反转字符串、颠倒链表等问题。
  3. 回文问题:判断一个字符串或子串是否是回文,或者找到最长回文子串时,双指针算法是一种常用的解决方案。例如,验证回文串、最长回文子串等问题。
  4. 快慢指针:在链表相关问题中,使用快慢指针可以判断是否存在环、找到链表的中间节点等。快慢指针是双指针算法的一种特殊形式。
  5. 归并排序:归并排序中的合并操作可以使用双指针来实现。通过将两个有序数组合并为一个有序数组,双指针可以高效地完成归并排序。
  6. 滑动窗口:滑动窗口是一种常见的算法技巧,用于处理数组或字符串的连续子序列问题。通过使用两个指针来维护窗口的起始位置和结束位置,可以高效地解决滑动窗口相关的问题。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容