首页 > 编程语言 >Manacher 算法

Manacher 算法

时间:2024-10-14 20:34:29浏览次数:1  
标签:字符 暴力 Manacher 复杂度 算法 端点 字符串 回文

\(Manacher\) 算法

\(Manacher(马拉车)\) 算法,是一种高效解决最长回文子串问题的算法。其 \(O(n)\) 的复杂度相较于暴力 \(O(n^2)\) 和字符串哈希 \(O(nlogn)\) 来说,快了不少。

算法实现:

首先说一下暴力的解法,对于每一个字符串上的字符,考虑以其为起点,向两边扩展。若字符串上回文串不多且长度普遍较短,则暴力的时间复杂度接近 \(O(n)\) 。当然,若其回文串数量较多且长度较长时,暴力的复杂度将退化成 \(O(n^2)\) 。

如上图,在计算以 \(i\) 和以 \(j\) 为中心的回文字符串时,重复计算了一段 \(w\) 区间,这也是暴力的效率低的原因,考虑对其进行优化。应该优化掉这种重复计算。

首先,同一其形式,由于回文字符串分为奇数长度的和偶数长度的,其中心也有所不同,所以先将待处理的字符串进行一个小处理。在开头和结尾处加上两个不同的字符,防止越界,在其中的每个字符中,加入其中不会出现的字符来分割每个字符。这样使的两种不同的字符串都会变为奇数字符串。

例:由 \(ABBA\) 变为 \(\$A\#B\#B\#A\&\) ,则其中心字符由 \(BB\) 变为 \(\#\) 省去了分类讨论,同时可证得这样初始化字符串并不会改变其的回文性质,即原先回文的子串在初始化后仍是回文的。

然后是对回文子串长度的统计,设 \(p[i]\) 表示以 \(i\) 为中心的回文串的半径,当然,我们还需要了解另一个重要的性质:回文的镜像也是回文,概括一下说的话大概是:对于一个回文字符串 \(s\) 来说,其中心字符左右两侧的回文字符串在另一侧必定会有一个相同的回文子串 ,我们可以利用这个性质,对于 \(p[i]\) 来说,已知 \(p[1],p[2],p[3]...p[i-1]\) ,令 \(R\) 为这之前的回文字符串的最右端点,\(C\) 是 \(R\) 所在的回文字符串的中心字符,即 \(p[C]\) 是一个已经求过的点, \(R\) 是其右端点且是已经求得的所有右端点中最大的, \(R=C+p[C]\) 。对于 \(R\) 左边的回文串来说,可以利用性质减少暴力的次数,从而优化程序。

当有节点 \(i<R\) 时,其分为两种情况(PS: \(j\) 为 \(i\) 关于 \(C\) 对称的回文子串),如下图:

1.若 \(j\) 的区间左端点大于区间 \(C\) 的左端点,即图上第一种情况,即 \(j \subseteq C\) ,那么由于 \((i+j)/2=C\) (中点坐标公式),可得 \(j=C \times 2-i\) 则 \(p[i]=p[C \times 2-i]\) ,后再用暴力扩展法扩展(由于不知道是否到达最长)。

2.若 \(j\) 的区间左端点小于区间 \(C\) 的左端点,即图上第二种情况,即 \(j \nsubseteq C\) ,由于性质条件不成立,则只能投影一部分,则可以被投影的区间为 \(p[i]=w=R-i=p[C]+C-i\) ,后再用暴力扩展法向后推。

这两种情况可以统一处理,取最小值,后再用暴力向外扩展即可。

当然,不可能全部都在其左边,当有节点 \(i \geqslant R\) 时,由于还没有计算到,则只能令 \(p[i]=1\) 后向后暴力扩展求 \(q[i]\) 。

我们会发现无论是那种情况,最后还是要回到暴力扩展法上,不禁担心其复杂度的正确性,关于其证明,后面再给出。

code

char s[maxn];          //要处理的字符串
char s1[maxn];         //初始化后的字符串
int n,ans;             //字符串长度,答案
void in_it(){
	int k=0;
    s1[k++]='$';       //开头 
    s1[k++]='#';
    for(int i=0;i<n;i++){
        s1[k++]=s[i];
        s1[k++]='#';
	}
    s1[k++]='&';      //结尾
    n=k;              //更新字符串长度
}
void manacher(){
    int r=0,c;        //初始化最右点,
    for(int i=1;i<n;i++){
        //分类讨论
        if(i<r) p[i]=min(p[(c<<1)-i],p[c]+c-i);
        else    p[i]=1;
        //暴力向两边扩展
        while(s1[i+p[i]]==s1[i-p[i]]) p[i]++;
        if(p[i]+i>r){     //若超出最右点
            c=i;
            r=p[i]+i;     //更新
        }
    }
}
signed main(){
	cin>>s;
    n=strlen(s);
    in_it();
    manacher();
    for(int i=0;i<n;i++){
        ans=max(ans,p[i]); //更新ans
    }
	cout<<ans-1;
}

在看完代码后,我们发现其实执行 \(manacher\) 函数的过程其实是不断计算 \(p[i]\) ,实际是向右推进 \(R\) 的过程。由于 \(R\) 不会重复遍历,所以其复杂度为 \(O(n)\) 。

标签:字符,暴力,Manacher,复杂度,算法,端点,字符串,回文
From: https://www.cnblogs.com/adsd45666/p/18466024

相关文章

  • C学习笔记 基础算法整理 (10.9 - )(正学习,持续更新中)
    本文涵盖了适合初学者学习的基础、经典算法。包括:递归递推、排序、搜索/查找、枚举、图/树遍历、动态规划等。推荐了解  C语言各部分基本知识  后进行学习。学习使用算法,可以:了解针对某类问题的通用解决方案提高逻辑思维能力将复杂的任务分解为简单的步骤精简代码,避免......
  • 代码随想录算法训练营第八天(1)|哈希表理论基础
    文档讲解:代码随想录难度:有一点哈希表理论基础哈希表首先什么是哈希表,哈希表(英文名字为Hashtable,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道都是指hashtable就可以了)。哈希表是根据关键码的值而直接进行访问的数据结构。这么官方的解释可能有点懵,其实......
  • 双向RRT路径搜索算法matlab代码
    一、实现效果见:双向RRT路径搜索算法流程、注意事项-CSDN博客二、代码完整代码见:https://download.csdn.net/download/m0_69611489/89882862clcclearcloseall%%设置环境S=[0.50.5];E=[99.599.5];Limit=[01000100];ob=[5050;7080];ob_r=[15;7];step=1;iu......
  • 2025秋招NLP算法面试真题(二十二)-大模型参数高效微调技术总结与对比
    目录当前高效微调技术的简述BitFitPrefixTuningPromptTuningP-TuningP-Tuningv2AdapterTuningAdapterFusionAdapterDropLoRAAdaLoRA<......
  • 算法题——合并两个已排序链表(不使用额外空间)
    题目如下:        输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。        数据范围: 0≤n≤10000≤n≤1000,−1000≤节点值≤1000−1000≤节点值≤1000要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)      ......
  • 强化学习性能指标之一:以训练的episodes数和训练所需样本数作为评价算法性能的指标
    在强化学习领域,一般都是限定训练的episodes数和训练所需样本数的,也就是说在进行算法性能对比的时候各个算法都是在相同的训练的episodes数和训练所需样本数的,但是如果我们在算法得分数保持相同的情况下是不是可以将各个算法所用的不同的训练的episodes数和训练所需样本数作为性能......
  • 机器学习领域如何判定算法是否收敛(算法是否稳定)
    最近在看ML的资料的时候看到有关算法收敛的讨论,然后有些资料并没有说明如何判定算法是否收敛,甚至有些资料中会将算法收敛和算法稳定性放在同等位置上来进行讨论,为此本文就这个问题进行一些讨论。几年前参加实验室大师兄小利师兄的博士论文答辩的时候有答辩评委提出了这么一个问......
  • 机器学习_线性回归_岭回归算法预测波士顿房价代码实现(机器学习全流程)(附带数据集hou
    #1.导入外部数据集HousingDataimportpandasaspdboston_data=pd.read_csv(r"C:\Users\鹰\Desktop\ML_Set\HousingData.csv")#数据基本描述print(boston_data.head())print(boston_data.describe())print(boston_data.shape)#2.数据基本处理-缺失值处理,特征值和......
  • Pytho逻辑回归算法:面向对象的实现与案例详解
    这里写目录标题Python逻辑回归算法:面向对象的实现与案例详解引言一、逻辑回归算法简介1.1损失函数1.2梯度下降二、面向对象的逻辑回归实现2.1类的设计2.2Python代码实现2.3代码详解三、逻辑回归案例分析3.1案例一:简单二分类问题问题描述数据代码实现输出结果3问......
  • Python决策树算法:面向对象的实现与案例详解
    目录Python决策树算法:面向对象的实现与案例详解引言一、决策树算法概述1.1决策树的基本思想1.2分类与回归树1.3决策树的构建过程1.4决策树的优缺点优点缺点二、面向对象的决策树实现2.1类的设计2.2Python代码实现2.3代码详解三、案例分析3.1案例一:鸢尾花分类......