马拉车算法Kimi详解(js版)

马拉车算法(Manacher's Algorithm)讲解

马拉车算法是一种用于寻找字符串中最长回文子串的高效算法。它的核心思想是利用回文的对称性,避免不必要的重复计算,从而将时间复杂度降低到 (O(n))。

1. 问题背景

给定一个字符串 ( s ),要求找到其中最长的回文子串。回文子串是指正读和反读都相同的子串。

2. 马拉车算法的核心思想

马拉车算法通过在字符串中插入特殊字符(如 #),将原字符串转换为一个长度为奇数的新字符串,从而简化回文中心的处理。

2.1 插入特殊字符

假设原字符串为 s,长度为 ( n )。我们可以在每个字符之间和字符串的首尾插入特殊字符 #,将原字符串转换为新字符串 t,长度为 ( 2n + 1 )。

例如:

  • 原字符串:s = "abba"
  • 转换后:t = "#a#b#b#a#"

这样做的好处是,回文中心可以统一处理为一个字符,避免了奇偶长度的区分。

2.2 定义回文半径

对于新字符串 t 中的每个字符 ( t[i] ),定义 ( P[i] ) 为以 ( t[i] ) 为中心的最长回文子串的半径。例如:

  • 如果 ( t[i] = 'a' ),且以 ( t[i] ) 为中心的最长回文子串为 "#a#",则 ( P[i] = 1 )。
2.3 利用对称性

假设我们已经计算了 ( P[i] ),并且知道以 ( t[i] ) 为中心的最长回文子串。当计算 ( P[j] ) 时,如果 ( j ) 在以 ( t[i] ) 为中心的回文子串内,可以通过对称性快速计算 ( P[j] )。

具体来说:

  • 如果 ( j ) 在以 ( t[i] ) 为中心的回文子串内,且 ( j ) 关于 ( i ) 的对称位置为 ( j' ),则 ( P[j] ) 至少为 ( P[j'] )。
  • 如果 ( j + P[j'] ) 超出了以 ( t[i] ) 为中心的回文子串范围,则需要进一步扩展 ( P[j] )。

3. 算法步骤

  1. 预处理字符串 :将原字符串 s 转换为新字符串 t,并在每个字符之间插入 #
  2. 初始化数组:定义数组 ( P ),其中 ( P[i] ) 表示以 ( t[i] ) 为中心的最长回文子串的半径。
  3. 遍历字符串
    • 维护两个变量:centerright,分别表示当前已知的最长回文子串的中心和右边界。
    • 对于每个位置 ( i ):
      • 如果 ( i ) 在 right 的范围内,计算 P[i] 的初始值: P[i]=min(P[2×center−i],right−i)
      • 如果 ( i ) 超出 right 的范围,初始化 P[i] = 0
      • 从 ( i + P[i] + 1 ) 开始扩展,直到不满足回文条件。
      • 更新 P[i]
      • 如果 ( i + P[i] > right ),更新 center = iright = i + P[i]
  4. 计算最长回文子串 :遍历 P 数组,找到最大值及其位置,从而确定最长回文子串。

4. JavaScript 代码实现

javascript 复制代码
function longestPalindromicSubstring(s) {
    // 预处理字符串
    const t = `#${s.split('').join('#')}#`;
    const n = t.length;
    const P = new Array(n).fill(0);
    let center = 0, right = 0;
    let maxLen = 0, centerIndex = 0;

    for (let i = 0; i < n; i++) {
        // 如果当前字符在已知的最长回文子串范围内,利用对称性计算初始 P[i]
        if (i < right) {
            const mirror = 2 * center - i;
            P[i] = Math.min(P[mirror], right - i);
        }

        // 尝试扩展回文子串
        while (i - P[i] - 1 >= 0 && i + P[i] + 1 < n && t[i - P[i] - 1] === t[i + P[i] + 1]) {
            P[i]++;
        }

        // 如果扩展后的回文子串超出了已知的右边界,更新中心和右边界
        if (i + P[i] > right) {
            center = i;
            right = i + P[i];
        }

        // 更新最长回文子串的信息
        if (P[i] > maxLen) {
            maxLen = P[i];
            centerIndex = i;
        }
    }

    // 提取最长回文子串
    const start = (centerIndex - maxLen) >>> 1;
    return s.substring(start, start + maxLen);
}

// 示例
const s = "babad";
console.log(longestPalindromicSubstring(s)); // 输出 "bab" 或 "aba"

代码说明

  1. 预处理字符串 :通过在每个字符之间插入 #,将原字符串转换为新字符串 t
  2. 初始化数组P 数组用于存储每个位置的回文半径。
  3. 遍历字符串
    • 使用 centerright 维护当前已知的最长回文子串的中心和右边界。
    • 对于每个位置 i,根据是否在 right 范围内,计算 P[i] 的初始值。
    • 通过扩展回文子串更新 P[i],并根据需要更新 centerright
  4. 提取结果 :根据 P 数组找到最长回文子串的中心和长度,提取对应的子串。

时间复杂度分析

马拉车算法的时间复杂度为 (O(n)),其中 ( n ) 是新字符串 t 的长度。这是因为每个字符最多被扩展一次,且每次扩展操作的时间复杂度为 (O(1))。

个人总结

通过定义两个重要变量,回文中心位置以及回文半径,进行遍历计算。 优化计算的关键是运用上之前计算值,因此在遍历回文中心时,想办法用上之前的回文中心计算结果,找到两者之间关系(即参照之前某次回文中心位置和回文半径,是否能够直接求出当前回文中心的回文半径),进行优化。

相关推荐
拉不动的猪14 分钟前
前端自做埋点,我们应该要注意的几个问题
前端·javascript·面试
王景程23 分钟前
如何测试短信接口
java·服务器·前端
2301_8076114930 分钟前
77. 组合
c++·算法·leetcode·深度优先·回溯
安冬的码畜日常1 小时前
【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
开发语言·前端·人工智能·ai·扫雷游戏·ai辅助编程·辅助编程
小杨升级打怪中1 小时前
前端面经-JS篇(三)--事件、性能优化、防抖与节流
前端·javascript·xss
清风细雨_林木木1 小时前
Vue开发网站会有“#”原因是前端路由使用了 Hash 模式
前端·vue.js·哈希算法
鸿蒙布道师1 小时前
OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
前端·人工智能·chrome·深度学习·opencv·自然语言处理·chatgpt
袈裟和尚1 小时前
如何在安卓平板上下载安装Google Chrome【轻松安装】
前端·chrome·电脑
曹牧1 小时前
HTML字符实体和转义字符串
前端·html
小希爸爸2 小时前
2、中医基础入门和养生
前端·后端