马拉车算法

算法简介

马拉车算法是用来找字符串中的最大回文子串的算法

初始化字符串

回文可能面对奇偶回文串的问题,可以将字符串特殊处理全部变成偶数或奇数,我这是直接在首尾和每个字符前加上#变成偶数

例如aabacba 转换成##a#a#b#a#c#b#a#

中心拓展

由第一个字符出发,遍历一遍字符串,并且以这个字符为中心向两边拓展,找回文半径,并确定回文右边界

我们没有必要为每一个字符的回文半径从0开始拓展,每一个字符的回文半径都可以根据对称性先来确定一个最初半径,然后拓展,这样可以节省很多时间

设回文中心为mid,mid的回文半径为R,最大右边界就是mid+R

当i < mid+R时i关于mid的对称点为j=2*mid-i 由对称可知假如j是一个回文中心,那么在以i为中心的字符串也可能存在回文,所以i的回文半径为min(以j为中心的回文半径,i到mid+R的距离),因为超出了mid的回文半径的话,就不能根据对称来确定了,所以先暂时确定i的回文半径

当 i>=mid+R时,就只能自己回文了,半径为1

每次i的回文半径超出了原来的最大回文右边界,就要更新mid和边界

确定回文串起点和长度

以每一个字符为中心的回文长度就是回文半径-1了,因为有#符号在相当于把字符长度翻倍了,同理也不难推出回文起点

#include <iostream>
#include <string>
#include <cmath>
#include <cstring>

using namespace std;

string init(string in)
{
    string str("##");
    for (string::iterator it = in.begin(); it != in.end(); it++)
    {
        str.append(1, *it);
        str.append("#");
    }
    return str;
}
int *manacher(string &str)
{
    int *radius;
    int mr = 0;   //maxright 为回文字符串的右边界
    int mid=0;   //回文的中点
    int n = str.length();
    radius = new int[n + 1]();
    for (int i = 1; i < n; i++)
    {
        if (i < mr)
        {
            // i 一定大于mid
            //当i小于右边界时根据对称求得一部分回文半径,后面在for循环里继续向两边拓展可以较快地求得回文半径
            //这里min很灵活,因为我们不知道右边界右边的情况,所以我们取对称点的回文半径和i到右边界之间的距离的较小的那一个
            radius[i] = min(radius[(mid << 1) - i], radius[mid] + mid - i);
        }
        else
        {
            //当i超出了回文的右边界,说明前面不构成回文条件,只有自己与自己回文
            radius[i] = 1;
        }

        for (; str[i + radius[i]] == str[i - radius[i]]; radius[i]++)
            ;//向两边拓展,半径增加
        if (radius[i] + i > mr)
        {
            //更新边界和中心
            mr = radius[i] + i;
            mid = i;
        }
    }
    return radius;
}
int main()
{
    string str;
    cin >> str;
    str = init(str);
    // cout << str << endl;
    // return 0;
    int *radius = manacher(str);
    int ans = 1;
    int index = 0;
    int n = str.length();
    for (int i = 0; i < n; i++)
    {
        if (radius[i] > ans)
        {
            ans = radius[i];
            index = i;
        }
    }
    if (ans == 2)
    {
        cout << 0 << endl;
    }
    else
    {
        cout << ans - 1 << endl;
        for (int i = index - ans + 2; i < index + ans; i += 2)
        {
            cout << str[i];
        }
        return 0;
    }
}

结果

aAabccbaABcdcBA
8
AabccbaA
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值