manacher(马拉车)算法C++详解
2023-08-10 16:36:56  来源: 博客园  
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 #include #include #include 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;}

关键词:

责编: