编程 如何在 JavaScript 字符串中找到最长的回文子字符串?

2025-06-28 17:55:21 +0800 CST views 21

如何在 JavaScript 字符串中找到最长的回文子字符串?

在字符串处理问题中,回文字符串(Palindrome) 是一种非常经典的模式:一个字符串如果正着读和反着读完全一样,它就是回文字符串,例如 'racecar''abba'

在实际开发中,我们经常需要找到一个字符串中最长的回文子字符串(Longest Palindromic Substring),这不仅考验我们的算法能力,也训练我们对于双指针、中心扩展等技术的掌握。

⚠ 本文不涉及 Manacher 算法(最优 O(n) 解决方案),而是注重于常见算法思维和实战技巧的掌握。


🔍 一、什么是回文字符串?

判断一个字符串是否是回文非常简单。可以直接反转它然后与原始字符串进行比较:

const isPalindrome = str => {
  const reversed = str.split('').reverse().join('');
  return str === reversed;
};

isPalindrome('racecar'); // true
isPalindrome('abba');    // true
isPalindrome('hello');   // false

🧨 二、暴力破解法(Brute Force)

最直观的方式是穷举所有可能的子字符串,然后依次判断它们是否是回文,保留其中最长的那一个。

实现思路:

  1. 使用两个指针枚举所有子串。
  2. 检查是否为回文。
  3. 更新最长结果。
const longestPalindrome = str => {
  let best = { length: 0, value: '' };
  for (let l = 0; l < str.length; l++)
    for (let r = l + best.length; r < str.length; r++) {
      const current = { length: r - l, value: str.slice(l, r + 1) };
      if (!isPalindrome(current.value)) continue;
      if (current.length < best.length) continue;
      best = current;
    }
  return best.value;
};

longestPalindrome('babad'); // 输出 'bab' 或 'aba'
longestPalindrome('cbbd');  // 输出 'bb'

复杂度分析

  • 时间复杂度:O(n^3)
  • 空间复杂度:O(n)(字符串切片时)

❗暴力法虽然直观,但效率非常低,无法应对大规模数据。


💡 三、中心扩展法(Expand Around Center)

我们可以将每个字符(甚至字符间的空隙)作为“回文中心”,从中心向两边扩展,查找最大长度的回文。

回文扩展核心函数:

const expandAroundCenter = (str, left, right) => {
  while (left >= 0 && right < str.length && str[left] === str[right]) {
    left--;
    right++;
  }
  return {
    left: left + 1,
    right: right - 1,
    length: right - left - 1
  };
};

判断字符串是否为回文:

const isPalindrome = str => {
  const centerPoints = (str.length % 2 === 0)
    ? [str.length / 2 - 1, str.length / 2]
    : [Math.floor(str.length / 2), Math.floor(str.length / 2)];
  const maxPalindrome = expandAroundCenter(str, ...centerPoints);
  return maxPalindrome.length === str.length;
};

🚀 四、最终高效实现(推荐方法)

将中心扩展方法应用到字符串的每一个位置,以捕获最长的回文子串。

const expandAroundCenter = (str, left, right) => {
  while (left >= 0 && right < str.length && str[left] === str[right]) {
    left--;
    right++;
  }
  return { left: left + 1, right: right - 1, length: right - left - 1 };
};

const longestPalindrome = str => {
  let best = { left: 0, right: 0, length: 0 };
  for (let i = 0; i < str.length; i++) {
    const odd = expandAroundCenter(str, i, i);
    const even = expandAroundCenter(str, i, i + 1);
    const current = odd.length > even.length ? odd : even;
    if (current.length > best.length) best = current;
  }
  return str.slice(best.left, best.right + 1);
};

longestPalindrome('babad'); // 输出 'bab' 或 'aba'
longestPalindrome('cbbd');  // 输出 'bb'

复杂度分析

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

🧠 五、总结与启发

我们一步步从最基础的暴力破解,优化到基于中心扩展的高效解法。虽然这并非理论上的最优(Manacher 算法可达到 O(n)),但在实际开发中已足够高效且易于实现。

技术要点回顾:

  • 双指针技巧 在字符串问题中极其重要。
  • 从中心扩展法 是解决对称性问题的通用思路。
  • 不要一开始就追求最优算法,先实现可行解再逐步优化
复制全文 生成海报 算法 字符串处理 JavaScript

推荐文章

前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
Nginx 反向代理 Redis 服务
2024-11-19 09:41:21 +0800 CST
如何在Vue中处理动态路由?
2024-11-19 06:09:50 +0800 CST
api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
js函数常见的写法以及调用方法
2024-11-19 08:55:17 +0800 CST
使用 Nginx 获取客户端真实 IP
2024-11-18 14:51:58 +0800 CST
HTML和CSS创建的弹性菜单
2024-11-19 10:09:04 +0800 CST
Vue3中的响应式原理是什么?
2024-11-19 09:43:12 +0800 CST
File 和 Blob 的区别
2024-11-18 23:11:46 +0800 CST
Golang在整洁架构中优雅使用事务
2024-11-18 19:26:04 +0800 CST
Rust 高性能 XML 读写库
2024-11-19 07:50:32 +0800 CST
JavaScript设计模式:单例模式
2024-11-18 10:57:41 +0800 CST
避免 Go 语言中的接口污染
2024-11-19 05:20:53 +0800 CST
Vue3中如何处理路由和导航?
2024-11-18 16:56:14 +0800 CST
JavaScript 策略模式
2024-11-19 07:34:29 +0800 CST
JavaScript设计模式:装饰器模式
2024-11-19 06:05:51 +0800 CST
ElasticSearch集群搭建指南
2024-11-19 02:31:21 +0800 CST
介绍Vue3的Tree Shaking是什么?
2024-11-18 20:37:41 +0800 CST
2025,重新认识 HTML!
2025-02-07 14:40:00 +0800 CST
智慧加水系统
2024-11-19 06:33:36 +0800 CST
程序员茄子在线接单