算法导论:回文子串(Manacher)算法 ,O(n)时间效率实现

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

问题描述:

输入一个字符串,求出其中最大的回文子串。子串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同,如abba和yyxyy。

解析:

这里介绍O(n)回文子串(Manacher)算法

算法基本要点:首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度

在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。

为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。
下面以字符串12212321为例,经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";

然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:

S     #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #
P     1  2  1  2  5  2  1  4  1  2  1  6  1  2  1  2  1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度

下面计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

这个算法的关键点就在这里了:如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。

具体代码如下:

if(mx > i)
{
      p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));
}
else
{
       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之后的部分是否对称,就只能一个一个匹配了。

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了

下面给出原文,进一步解释算法为线性的原因

源代码:

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

using namespace std;

void findBMstr(string& str)
{
    int *p = new int[str.size() + 1];
    memset(p, 0, sizeof(p));

    int mx = 0, id = 0;
    for(int i = 1; i <=  str.size(); i++)
    {
        if(mx > i)
        {
            p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));
        }
        else
        {
            p[i] = 1;
        }

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

        if(i + p[i] > mx)
        {
            mx = i + p[i];
            id = i;
        }

    }
    int max = 0, ii;
    for(int i = 1; i < str.size(); i++)
    {
        if(p[i] > max)
        {
            ii = i;
            max = p[i];
        }
    }

    max--;

    int start = ii - max ;
    int end = ii + max;
    for(int i = start; i <= end; i++)
    {
        if(str[i] != '#')
        {
            cout << str[i];
        }
    }
    cout << endl;

    delete  p;
}

int main()
{
    string str = "12212321";
    string str0;
    str0 += "$#";
    for(int i = 0; i < str.size(); i++)
    {
        str0 += str[i];
        str0 += "#";
    }

    cout << str0 << endl;
    findBMstr(str0);
    return 0;
}

执行结果:



[算法导论] 5.最长回文子串 0、题目 1、动态规划 复杂度分析 时间复杂度:O(n^2),其中 n是字符串的长度。动态规划的状态总数为 O(n^2),对于每个状态,我们需要转移的时间为 O(1)。 空间复杂度:O(n^2),即存储动态规划状态需要的空间。 class Solution: def longestPalindrome(self, s): n = len(s) if n < 2: return s ma.. 阅读详情

相关推荐

Manacher算法:求解最长回文字符串,时间复杂度为O(N)

Manacher算法:求解最长回文字符串,时间复杂度为O(N) 回文串定义:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串回文子串,顾名思义,即字符串中满足回文性质的子串。

回文子串算法

法一:简单方法             两层循环对字符串中所有的子串进行定位,进而判断是否是回文子串,复杂度O(n^3)。            方法简单,易想到,但暴力,易超时。         for(i=0;i&lt;m;i++) { for(j=i;j&lt;m;j++) { int ok=1; ...

Moeyinss 2401

属性基加密(Attribute-based Encryption, ABE)

KP-ABE中,用户的密钥定义了访问策略,密文的属性与策略进行匹配。CP-ABE中,密文定义了访问策略,用户的属性与策略进行匹配。

2201_75392924的博客 1803

LeetCode 647 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 示例 1: 输入:"abc" 输出:3 解释:三个回文子串: "a", "b", "c" 示例 2: 输入:"aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa" 首先是无脑动态规划: class Solution: def countSubstrings(self, s: str) -&g

weixin_42379312的博客 248

经典算法思想题目-回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

ybigbear的博客 724

关于回文串的几种常见算法总结

目录一、判断字符串为回文串二、查找字符串中有多少个回文子串1、暴力枚举2、中心扩展法 一、判断字符串为回文串 回文串:字符串反转后和原字符串相同同,比如aba 判断代码如下,思路是从头尾开始依次比较,全部相同就代表是回文串 时间复杂度为O(n),空间复杂度O(1) //判断字符串i-j是否为回文串 bool isPalindromic(string &s,int i,int j) { while(i < j) { i

A_wangc的博客 1万+

Manacher 算法

本文介绍了高效解决回文串问题的Manacher算法。该算法通过构建回文半径数组,利用最右回文串加速填表过程,将时间复杂度优化至O(n)。文章详细讲解了算法原理(包括四种情况分类讨论)、预处理方法、代码实现(仅需4行核心代码)以及模板题目应用。相比O()的中心扩展算法Manacher算法通过避免指针回退显著提升效率,适合处理大规模数据。作者建议读者通过刻意练习掌握这一算法

2403_88695928的博客 1577

【最长回文子串Manacher算法详解

写在前面 manacher算法解决最长回文子串以及变形问题的时间复杂度为O(n)。 如果你想囫囵吞枣,只需要使用到该算法,你可以直接把代码拿走;但如果你想深入了解这个算法的工作原理和关键部分解读,还是希望你能静下心来,拿出一张纸,一根笔,来好好揣摩算法背后的魅力,算法解读部分内容来自《来自于程序员代码面试指南:IT名企算法与数据结构题目最优解》,理解起来的确有点冗杂和费力。(以下来自网友真实案例) Python代码(Leetcode 5为例) class Solution: def.

ZoomToday 1637

[算法] - Manacher算法【 O(N) 最长回文子串的长度】

目录 Manacher算法解决的问题字符串str中, 最长回文子串的长度如何求解?如何做到时间复杂度O(N)完成? 1. 大体理解: 2. 思路剖析 3. 时间复杂度 4. 详细代码 Manacher算法解决的问题字符串str中, 最长回文子串的长度如何求解? 如何做到时间复杂度O(N)完成? 1. 大体理解: 9是指回文子串多的长度 常用的方法:字符串的问题,...

Snoopy_Dream 394

求最长回文子串,O(n)复杂度

最长回文子串问题—Manacher算法 最长回文串问题是一个经典的算法题。 0. 问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度。 如果一个字符串正着读和反着读是一样的,那它就是回文串。下面是一些回文串的实例: 12321 a aba abba aaaa tattarrattat(牛津英语词典中最长的回文单词)

Avenger_Tao的专栏 4618

O(N)最长回文子串算法——Manacher算法

题目举例:http://hihocoder.com/problemset/problem/1032?sid=763559 题意:在一串连续的字符串中寻找它的最长子串(Longest Palindromic Substring) 输入:先从标准输入读取一个整数N(N,代表我给你的字符串的个数,然后接下来的就是我要给你的那N个字符串(字符串长度 输出:最长回文子串长度   题目分析:很自然地

Richard. Step by step 890

5分钟搞懂Manacher算法:如何用Python实现线性时间最长回文子串查找

本文详细解析了Manacher算法(马拉车算法)在Python中的实现,用于线性时间查找最长回文子串。通过预处理字符串和利用对称性,该算法时间复杂度优化至O(n),适用于大规模数据处理。文章包含完整代码实现、复杂度分析及实战应用,帮助开发者快速掌握这一高效算法

s8t9u0v1w的博客 894

最长回文子串问题-Manacher算法【建议收藏】

前面我们讲过一个关于字符串的算法:KMP算法。今天我们来讲另外一个字符串算法Manacher算法。这个算法是用于解决一个问题叫:最长回文子串。 前期文章:KMP算法 牛客网OJ链接 说的简单一点,给定一个字符串,返回的值是这个字符串的最长回文子串的长度。顾名思义,即是回文串,也是子串。 文章目录一、BF算法二、Manacher算法 一、BF算法 那上图的示例2为例:abc1234321ab。 最简单的思路就是从左到右遍历每一个字符。每来到一个字符位置,我们可以向左右两边进行扩展,分别比较左右两边的字符。

x0919的博客 999

Manacher 算法讲解

一:背景 给定一个字符串,求出其最长回文子串。例如: s="abcd",最长回文长度为 1; s="ababa",最长回文长度为 5; s="abccb",最长回文长度为 4,即bccb。 以上问题的传统思路大概是,遍历每一个字符,以该字符为中心向两边查找。其时间复杂度为$O(n^2)$,效率很差。 1975年,一个叫Manacher的人发明了一个算法Manacher算法(中文名:...

~ 1598

最长回文子串问题-Manacher算法深度解析

求解最长回文子串的传统方法如暴力枚举、动态规划虽然能解决问题,但在时间复杂度上存在一定局限。Manacher 算法以其巧妙的预处理方式和利用回文串对称性的优化策略,高效地解决了最长回文子串问题

1110

Manacher算法

时间复杂度内求解最长回文子串问题。其核心思想是利用回文串的对称性避免重复计算,通过动态维护当前最右回文边界来优化搜索过程。Manacher算法用于在。:将原字符串转换为奇数长度。对应原串最长回文子串长度。级别的字符串时,务必使用。

2403_88284978的博客 1377

Manacher 算法 求最长回文子串

刷LeetCode的时候有一题,提到了一个算法Manacher,可以在时间复杂度O(n)的情况下求出最长的回文子串,既然是一个没听过看过的算法,就去查了下并自己实现了下效果,在这纪录一下。 题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/ 回文子串:通俗的讲就是正序倒序都一样的字符串,例如aba,abba,...

王王王渣渣的博客 414

字符串——求最大回文子串——Manacher算法

1. Manacher 算法 Manacher算法主要用于处理字符串中求最长回文子串问题的算法。 传统的求最长回文子串算法,是先确定一个轴, 然后以该轴为中心向两边遍历判断是否相等,从而得到以该轴为中心最大回文子串,近而获取该字符串的最大回文子串。其时间复杂度为O(n^2)效率低,而且还要分类处理奇偶长度的回文串。 所以一个叫Manacher的人发明了一个算法,即Manacher算法,将时间复杂...

weixin_43326910的博客 521

【最长回文子串Manacher算法

文章目录题目:LeetCode.5 最长回文子串解法1:暴力法解法2:DP法求解最长公共子串解法3:Manacher算法 题目:LeetCode.5 最长回文子串 点击这里来查看题目来源。 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2: 输入: "...

timczm的博客 356
上一篇: 算法导论:快速找出无序数组中第k小的数
下一篇: C++ 栈和队列的介绍与使用
tham_
博客等级 码龄12年 233粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值