【算法日记】马拉车

Manacher马拉车算法

马拉车是时间复杂度为O(N)来求最长回文子串算法。

神奇的事情:我从来没学过马拉车

刷USACO题单刷到了马拉车

马拉车第一步

在字符中间加入未在字符串中出现的间隔符

如 abba

变 #a#b#b#a3

这样无论字符串是奇串还是偶串,都可转为奇串的形式。

对于数组 p[i],表示以t[i]字符为中心的回文子串的半径,若p[i] = 1,则该回文子串就是t[i]本身

两个辅助变量mx和id,其中id为最大回文子串中心的位置,mx是回文串能延伸到的最右端的位置

如果mx > i, 则 p[i] = min(p[2 * id - i], mx - i)

否则, p[i] = 1

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j]

当 P[j] >= mx - i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。

马拉车模板

#include<iostream>  
#include<string.h>
#include<algorithm>  
using namespace std;
char s[1000];
char s_new[2000];
int p[2000];
int Init()
{
    int len = strlen(s);
    s_new[0] = '$';
    s_new[1] = '#';
    int j = 2;
    for (int i = 0; i < len; i++)
    {
        s_new[j++] = s[i];
        s_new[j++] = '#';
    }
    s_new[j] = '\0';   
}
int Manacher()
{
    int len = Init();  //取得新字符串长度并完成向s_new的转换  
    int maxLen = -1;   //最长回文长度  
    int id;
    int mx = 0;
    for (int i = 1; i < len; i++)
    {
        if (i < mx)
            p[i] = min(p[2 * id - i], mx - i);  //需搞清楚上面那张图含义, mx和2*id-i的含义
        else
            p[i] = 1;
 
        while (s_new[i - p[i]] == s_new[i + p[i]])  //不需边界判断,因为左有'$',右有'\0'  
            p[i]++;
        //我们每走一步i,都要和mx比较,我们希望mx尽可能的远,这样才能更有机会执行if (i < mx)这句代码,从而提高效率 
        if (mx < i + p[i])  
        {
            id = i;
            mx = i + p[i];
        }
        maxLen = max(maxLen, p[i] - 1);
    }
    return maxLen;
}
int main()
{
    while (printf("请输入字符串:\n"))
    {
        scanf("%s", s);
        printf("最长回文长度为 %d\n\n", Manacher());
    }
    return 0;
}
posted @ 2020-11-16 09:58  Shayndel  阅读(63)  评论(0编辑  收藏  举报