HDU - 3068 最长回文(吊打网上其他马拉车算法讲解)

马拉车算法,原博主的链接:https://www.cnblogs.com/eternhope/p/9677502.html

只能说牛逼,也找不到其他形容词,,

如果一个回文子串的长度是偶数,对称轴会落在两个字符中间。

首先两个字符中间的这个位置就很难表示。

所以我们在两个字符中间加上没有用的字符,比如说'#'。开头结尾也加上。

例如:abcba --> #a#b#c#b#a#

这样我们能很方便的表示每一个位置。

manacher算法最终的目的是求出一个数组pl[i],代表以i为回文中心(也就是对称轴)的最长回文子串的回文半径。

回文半径指的是回文子串的长度除以二之后向上取整。

比如:#a#b#a#的回文半径就是4。

考虑用递推的方法求出pl数组。

首先我们知道pl[1]=1(特殊记一下)。

在递推的过程中维护一个np表示使i+pl[i]最大的一个i。

计算f[i]的时候,先考虑使用已知的信息求出f[i]。

如果i<=np+pl[np],意味着i被以np为回文中心的最长回文子串“覆盖”了。

下面的图用红色表示以np为回文中心的最长回文子串,用绿色表示以i为回文中心的最长回文子串。

这时有两种情况:

1.不仅i被覆盖,以i为回文中心的最长回文子串也被完全覆盖了。

这个时候由于对称性,pl[i]=pl[2*np-i]。

2.虽然i被覆盖,但是以i为回文中心的最长回文子串没有被完全覆盖。

这个时候我们只能保证np+pl[np]以内的对称性(蓝色部分)。

也就是说,pl[i]=pl[np]+np-i。

我们并不知道是哪种情况,所以只能对这两种情况分别求值并取其中的最小值。

之后如果还能继续向外拓展回文部分,就类似暴力的做法,一下一下向外拓展。

最后更新一下np。

求出的pl是适用于新串的(就是混了一堆‘#’的那个串)。

不难发现,对于原串,以i为中心的最长回文子串的长度为pl[i]-1,这个串的开始位置为(i-pl[i])/2+1(向下取整)。

大佬代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n;
char a[11000005];
char s[22000005];
int pl[22000005];

int main()
{
    scanf("%s",a+1);
    int al=strlen(a+1);
    for(int i=1;i<=al;i++)
        s[++n]='#',s[++n]=a[i];
    s[++n]='#';
    int np=1;
    pl[1]=1;
    for(int i=2;i<=n;i++)
    {
        pl[i]=min(pl[2*np-i],pl[np]+np-i);
        for(;i+pl[i]<=n&&s[i+pl[i]]==s[i-pl[i]];pl[i]++);
        if(i+pl[i]>np+pl[np])np=i;
    }
    int ans=0;
    for(int i=1;i<=n;i++)ans=max(ans,pl[i]-1);
    printf("%d",ans);
    return 0;
}

Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
Sample Input
aaaa
abab
Sample Output
4
6

膜拜大佬之后的题解

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string.h>
#include<iterator>
#include<vector>
#include<stdlib.h>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<sstream>
#define lowbit(x) (x&(-x))
typedef long long ll;
using namespace std;
int n;
char a[110010];
char s[220010];
int pl[220010];
int main()
{
    while(scanf("%s",a+1)!=EOF)
    {
        n = 0;
        int al = strlen(a+1);
        for(int i=1;i<=al;i++)
            s[++n] = '#',s[++n] = a[i];
        s[++n] = '#';
        int np = 1;
        pl[1] = 1;
        for(int i=2;i<=n;i++)
        {
            pl[i] = min(pl[2*np-i],pl[np]+np-i);
            for(;i+pl[i]<=n&&s[i+pl[i]]==s[i-pl[i]];pl[i]++);
            if(i+pl[i]>np+pl[np])
                np = i;
        }
        int ans = 0;
        for(int i=1;i<=n;i++)
            ans = max(ans,pl[i]-1);
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值