manacher(马拉车)算法C++详解
阅读原文时间:2023年08月11日阅读:1

马拉车的定义

马拉车本质是对中心扩展法(暴力算法)的优化。

马拉车是干什么的

Manacher算法帮助我们在给定的字符串中找到最长的回文子串

为了简单起见,我们先只处理有奇数个字符的字符串,关于偶数个字符的字符串,在文章最后会给出解法。我们的处理思路和暴力算法基本一致,那就是从左到右一个字符一个字符来处理这个字符串,寻找以当前处理的字符为中心的最长回文串,假设字符串的长度是N,那我们就在寻找到的N个最长回文串中取最长的就是答案了。

符号说明

我们约定,c是我们处理当前字符时,已经找到的右边界最大的回文字符串的中心。l和r分别是这个最长的回文字符串的左界和右界,也就是最左边的字符索引和最右边的字符索引。现在,我们举个例子来理解c、l和r。

例子:“abacabacabb”

当从左到右一个字符一个字符计算时,我们用i表示当前正在处理的字符的索引,当i在索引1时,最长的回文字符串是 "aba"(长度=3)。

当i在索引5时,如下图所示:

最长的回文字符串的答案是9,c、l、r的值如图中所示。

不难看出,c所代表的最长回文字符串

现在我们知道了c、l和r表示什么,为了下面算法的讲解更加自然,我们需要了解一个概念:镜像索引。

对于以c为中心的任何一个回文字符串来说 索引j关于c的镜像是j',如下图所示:

观察上图,不难得出下面的计算公式:

c - j = j' - c

此时,j的镜像j':

j' = (2 * c) - j

模板

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 3e5 + 5;
char s[maxn], t[maxn];
int p[maxn];

int main() {
    while (~scanf("%s", s)) {
        int l = strlen(s), len = 0;
        t[len++] = '$'; // 在字符串开头和结尾都添加一个特殊字符(如'$',
                        // 注意不要相同),使后续计算可以避免判断越界
        t[len++] = '#';
        for (int i = 0; i < l; ++i) {
            t[len++] = s[i];
            t[len++] = '#';
        }
        t[len] = '&';

        int center = 0, max_right = 0;
        for (int i = 1; i < len; ++i) {
            int i_mirror = 2 * center - i;
            if (max_right >
                i) { // 在最右边界的覆盖范围内, 就利用回文串特征计算长度
                p[i] = min(max_right - i,
                           p[i_mirror]); // 使用min()是为了防止超出最右边界
            } else {
                p[i] = 0;
            }
            while (t[i - 1 - p[i]] == t[i + 1 + p[i]])
                ++p[i];

            if (i + p[i] > max_right) { // 更新右边界的回文串中心
                center = i;
                max_right = i + p[i];
            }
        }

        int max_len = 0;
        // int pos = 0;  //记录最长回文子串中心的位置
        for (int i = 1; i < len; ++i)
            if (max_len < p[i]) {
                max_len = p[i];
                // pos = i;
            }
        printf("%d\n", max_len); //最长回文串的起始位置为: (pos - max_len) / 2
    }
    return 0;
}