P3805 manacher 算法

P3805 manacher 算法
时间限制(普通/Java):500MS/3000MS 内存限制:512.00MB
返回题目

描述

给出一个只由小写英文字符 a,b,c,…y,z 组成的字符串 S ,求 S 中最长回文串的长度 。

字符串长度为 n。(1<=n<=11000000)

输入

一行小写英文字符 a,b,c,⋯,y,z 组成的字符串 S。

输出

一个整数表示答案。

样例输入

aaa

样例输出

3

思路

manacher 算法模板题
洛谷的刚开始看看了好久没看懂,看这个知乎的算法详解一遍就看懂了
https://www.zhihu.com/question/37289584

下面通俗地讲一下算法大概,具体算法还是得看上面那个链接;

该算法目的是先找出 以 字符串中每个字符 和 相邻两个字符之间空隙 为对称中心的回文字符串的长度,并存入ans[n]数组里,最后遍历寻找最大的一个作为答案输出;

而该算法 妙就妙在如何计算这个数组;

因为考虑到可能以相邻两个字符之间空隙为对称中心,则在每个字符间插入一个题目给的字符串中不会出现的一个字符来表示该位置,为了防止后面比较越界,在字符串前后两端再各插入一个不同的字符(即首尾插入的俩字符互不相同,它俩 和 在俩字符中插入的那个字符 也不同,比如 abcba -> ~#a#b#c#b#a#$ 插入的 三类位置 字符都互不相同)

然后就是怎么计算这个数组:

用 i 去遍历该数组:从 i=1 到 len-1(即插入额外字符后的字符串的长度-1)。

这里先设了两个值 :c 和 r,代表当前 用来计算 ans[i] 的一个在计算 ans[i] 之前已经找出来的某一个回文字符串,c为对称中心位置,即下标;r为该回文字符串的右端点;
ans[i] 的计算都是通过 c 和 r 来计算的,而且 此时 i <= r ;如果 i 遍历时 i>r了 或者 在计算 ans[i] 时以 i 为中心的回文字符串的右端点越过了 r ,则需要更新c 和 r 的值,将此时 i 的位置赋给 c ,把 ans[i]赋给 r ,该更新后的新的回文字符串又可以用来计算后面 ans[i] 的值。

为什么能用 c 和 r 来计算 ans[i]的值:
在一个回文字符串中,以对称中心 为对称中心,对于一边的一个字符串区间,在另一边肯定会有一个的区间与其对称 ,当 这俩区间为为回文字符串,则俩 区间 就一样,
比如 abcbabcba -> a bcb a bcb a -> 中间a的两边的 bab都一样而且是回文;
则 先设 i 关于 c对称的点为 tempi
则在该确定的回文字符串 c 和 r 的范围之内 ,ans[tempi] == ans[i],但如果此时 以 i 为中心的回文字符串的右端点越过了 r,那么需要看看超过 r 的那部分字符串是否能算作ans[i]之中;

实际计算ans[i]时,是i遍历到某一位时,先ans[i]默认为0,然后将 i 关于 c 对称的 tempi 点的 ans[tempi] 与 r-i做比较,将较小的那个赋给ans[i];因为计算ans[i]是要在当前以c为中心,r为半径的已知的回文字符串中确定的,所以超过 r 到r右边部分的字符串没有遍历到,所以未知,就暂时把能确定的长度赋给ans[i]。

初步赋完值后再以i为中心,继续往两遍看看是否 以i为对称中心的字符串还能再往两边变长;

最后看看计算完 ans[i]后是否要更新 c 和 r ,然后进行下一步, i 继续往后遍历;

AC代码

#include <bits/stdc++.h>
using namespace std;
int ans[22000010];
string change(string s)
{
    string temp;
    temp+='~';
    int len=s.length();
    for(int i=0;i<len;i++)
        temp+="#",temp+=s[i];
    temp+="#_";
    return temp;
}
int solve(string s)
{
    int c=1,r=1;
    int len=s.length();
    for(int i=1;i<len;i++)
    {
        int tempi=2*c-i;
        ans[i]=min(ans[tempi],r-i);
        while (s[i+ans[i]+1]==s[i-ans[i]-1])
            ans[i]++;
        if(i+ans[i]>r)
        {
            c=i;
            r=i+ans[i];
        }
    }
    int res=0;
    for(int i=1;i<len;i++)
    {
        if(ans[i]>res)res=ans[i];
    }
    return res;
}
signed main()
{
    string s;
    cin>>s;
    int an;
    an=solve(change(s));
    cout<<an<<endl;
}
posted @ 2023-03-30 19:16  Minza  阅读(22)  评论(0编辑  收藏  举报