当前位置:首页 > C++ > 正文

Manacher算法详解(C++实现最长回文子串的高效解法)

在字符串处理中,最长回文子串是一个经典问题。暴力解法的时间复杂度高达 O(n³),即使优化后也仅能达到 O(n²)。而Manacher算法是一种线性时间复杂度 O(n) 的高效算法,专门用于解决此类问题。

本文将用通俗易懂的方式,手把手教你理解并用 C++ 实现 Manacher 算法,即使你是编程小白也能轻松掌握!

什么是回文?

回文是指正着读和反着读都一样的字符串,例如:"aba""abba""racecar" 等。

为什么需要 Manacher 算法?

传统的中心扩展法对每个字符都尝试向两边扩展,最坏情况下仍需 O(n²) 时间。而 Manacher算法 利用回文的对称性,避免重复计算,从而将时间复杂度降至 O(n)。

核心思想:预处理 + 利用对称性

Manacher 算法的关键在于两个技巧:

  1. 插入分隔符:将原字符串转换为奇数长度,统一处理奇偶回文。例如:"abba" → "#a#b#b#a#"
  2. 维护最右回文边界 R 和中心 C:利用已知回文信息加速后续计算。
Manacher算法详解(C++实现最长回文子串的高效解法) Manacher算法 C++回文字符串 最长回文子串 字符串算法 第1张

算法步骤详解

假设原字符串为 s,我们构造新字符串 t,并在其上定义数组 P,其中 P[i] 表示以 t[i] 为中心的最长回文半径(包含中心)。

算法流程如下:

  1. 预处理:在 s 的每个字符间插入特殊字符(如 '#'),首尾也加哨兵(如 '^''$' 防越界)。
  2. 初始化:C = 0(当前回文中心),R = 0(当前最右边界)。
  3. 遍历 t 中每个位置 i
    • i < R,则利用对称性:P[i] = min(R - i, P[2*C - i])
    • 尝试以 i 为中心向外扩展,更新 P[i]
    • 若扩展后超出 R,更新 C = iR = i + P[i]
  4. 最终答案:原字符串中最长回文子串长度为 max(P) - 1

C++ 代码实现

下面是一个完整的、可运行的 C++ 实现:

#include <iostream>#include <string>#include <vector>#include <algorithm>using namespace std;string preprocess(const string& s) {    // 在字符串前后加上哨兵,中间插入 '#'    string t = "^#";    for (char c : s) {        t += c;        t += '#';    }    t += "$";    return t;}string longestPalindrome(const string& s) {    if (s.empty()) return "";    string t = preprocess(s);    int n = t.length();    vector<int> P(n, 0);    int C = 0, R = 0; // 当前中心和最右边界    for (int i = 1; i < n - 1; i++) {        // 利用对称性        int mirror = 2 * C - i;        if (i < R) {            P[i] = min(R - i, P[mirror]);        }        // 尝试扩展        while (t[i + (1 + P[i])] == t[i - (1 + P[i])]) {            P[i]++;        }        // 更新中心和右边界        if (i + P[i] > R) {            C = i;            R = i + P[i];        }    }    // 找到最大回文半径及其位置    int maxLen = 0, centerIndex = 0;    for (int i = 1; i < n - 1; i++) {        if (P[i] > maxLen) {            maxLen = P[i];            centerIndex = i;        }    }    // 转换回原字符串的起始位置    int start = (centerIndex - maxLen) / 2;    return s.substr(start, maxLen);}int main() {    string s = "babad";    cout << "最长回文子串: " << longestPalindrome(s) << endl;    return 0;}  

关键点解析

  • 预处理:通过插入 '#',将所有回文统一为奇数长度,简化逻辑。
  • 对称性利用:当 iR 内时,其对称点 mirror 的回文信息可直接复用,但不能超过 R - i
  • 线性时间:每个字符最多被访问两次(一次扩展,一次被跳过),因此总时间复杂度为 O(n)。

总结

Manacher算法 是解决 最长回文子串 问题的最优解之一,特别适合在竞赛或高性能场景中使用。通过本文的讲解和 C++ 代码示例,相信你已经掌握了这一强大工具。

记住,理解算法的核心思想比死记代码更重要。多动手实践,尝试修改输入字符串,观察 P 数组的变化,你会对 字符串算法 有更深的体会!

希望这篇教程对你有帮助!如果你喜欢,请分享给更多学习 C++回文字符串 处理的朋友吧!