最长回文子串 manacher算法

本文介绍了一种复杂度为O(n)的求最长回文子串算法,通过利用之前遍历得到的回文信息来提高效率。算法首先确定回文子串的中心,然后向两边扩展来寻找最大的回文串。为了处理偶数长度的回文串,通常会在字符串中插入特殊字符,使其变为奇数长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求最长回文子串比较有名的一种算法,复杂度是O(n)的,(不要问我为什么是O(n))。

思路:尽量利用到之前遍历得到的回文信息。
表示以i为中心,最长的回文子串的半径有多长,并保存当前的回文子串往右延伸,最长能延伸到哪,即p[i]+i的最大值,存为right,其回文子串的中心点存为index。
考虑当下标为i时,要求p[i]。有几种情况:
1. 当i > right时,超出了之前得到的信息的范围,只能从i开始向两边遍历,求最长的回文范围了;
这里写图片描述
2. 当i < right时,说明现在还在之前遍历过的范围内,那么就把之前的信息利用起来。
这里写图片描述
p[i]最大可能为多大? 考虑一下以index为对称中心的i ‘,如果i ’ - p[i ‘] > left,也就是说i ‘为中心的最长回文子串完全被left-right包含,那么因为是对称的,所以i为中心的最长回文串也就是p[i ‘];如果i ’ - p[i ‘] < left,也就是说i ’ 为中心的最长回文子串超出了left-right的范围,那么以i为中心的回文串能超出right的范围吗。答案是不能,看一下图就知道如果超出了,两边又是对称的,那么left-right就延长了,而left-right已经是以i为中心的最长回文子串了,所以p[i] = min{p[i ‘], right - i}。

上面的方法,回文串必须是奇数长的,因为有中心点嘛,为了代码好些,普遍采用的放大是加特殊字符,将偶数长的也变成奇数长的。
这里写图片描述
代码如下:

class Solution {
public:
    string pre(string s)
    {
        string res = "$";
        for (int i = 0; i<s.size(); i++)
        {
            res += "#";
            res += s[i];
        }
        res += "#";
        res += "\0";
        return res;
    }
    string longestPalindrome(string s) {
        s = pre(s);

        //        cout << s << endl;

        int p[100000] = { 0 };

        int right = 0, index = 0;

        int max = 0;
        int max_i = 0;

        for (int i = 1; i<s.size(); i++)
        {
            if (right > i)
                p[i] = min(p[2 * index - i], right - i);
            else
                p[i] = 1;

            while (s[i + p[i]] == s[i - p[i]])
                p[i]++;

            if (i + p[i] > right)
            {
                right = i + p[i];
                index = i;
            }

            if (p[i]>max)
            {
                max = p[i];
                max_i = i;
            }

        }

        string res = "";

        for (int i = max_i - p[max_i] + 1; i<max_i + p[max_i]; i++)
            if (s[i] != '#')
                res += s[i];

        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值