马拉车算法模板

2024-01-26 11:58
文章标签 算法 模板 拉车

本文主要是介绍马拉车算法模板,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大佬博客

马拉车用于解决最长回文子串问题,重点是子串,而不是子序列,想了解最长回文子序列的可以看下这篇博客传送门。对于这种问题,当然最简单粗暴的方法就是暴力求解,但太暴力也不好,毕竟会TLE。所以对于求最长回文子串的问题有一种神奇的算法——马拉车算法,神奇就神奇在时间复杂度为O(n)。 我先说一下大概思路,就是用一个Len[i]数组去存第i个位置到mx位置的长度,然后用id记录上一次操作的位置,mx标记上一次的最长子串的最右端,然后依次去递推。不太好理解(花了好几个小时才懂),现在看不懂没关系,我尽量讲的详细点。
以hihoCoder的一道裸题为例,只要能AC就差不多懂了,题目链接:传送门
先定义一个字符串s = “ababc”,因为对于回文字符串来说有对称轴,比如说aba就是以b作为对称轴,那么当字符串长度为偶数的时候,对称轴就不是唯一的整数位了。所以我们要对原字符串
做一个预处理,在每个字符左右都加上一个特殊字符,这里我用’#’,那么处理后的字符串str就是#a#b#a#b#c#了,不管原字符串长度是奇数还是偶数,处理后的长度都变成奇数了。然后还需要在这个str的最前面加一个不同于之前的特殊字符的特殊字符,这里我用了’%’(因为在后面的while循环中在匹配字符的时候可能会越界)。这样预处理操作就完了,最后的字符串str就是%#a#b#a#b#c#了。

首先需要明白的是,我定义的mx是上一次操作的最长回文子串的最右端,我解释一下,比如说ababa,第一次对i=2操作的时候,会计算出他的最回文子串为aba(长度为3),则在这次操作结束后mx的位置就是4,也就是aba的下一位。而我定义的id是上一次操作的位置,也就是i的位置,还以刚才的例子为例,在对i=2操作结束后,id的位置就是2。姑且先这么理解,知道他是什么就行,后面再去思考。

Len数组里存的是第i个位置到mx位置的长度,比如说还是ababa,当i=2的时候,可以计算出aba是它此时的最长回文子串,那么mx的位置就是aba的下一位,那Len[2]存的就是mx - i了。最重要的就是Len数组,所以这点一定要弄明白,Len存的是,当前这个位置到它的最长回文子串的最右端的距离(也就是mx的位置)。在str字符串中,Len[i] = 2*Len[i] - 1(因为有特殊字符),在s字符串中Len[i] = mx - id + 1。

知道这些后,开始进入正题,先看下核心代码。
在这里插入图片描述

首先把mx初始化0,然后开始从i=1开始遍历str,当i>=mx的时候,也就是i在mx前面的时候,就让Len[i] = 1,表示在i之前没有回文串出现,所以让Len从1开始,然后先不说i<mx的情况,然后往下走到while循环,这个循环就是用来判断这个位置的最长回文子串的,就是以这个点为中心,然后依次比较左边一个和右边一个、左边第二个和右边第二个…是否相等,相等的话就让Len[i]++,就相当于这个位置到这个位置的子串的最右端的长度++。然后再比较这个点+这个点的回文长度是否超过了mx的位置,超过的话就更新一下mx的值,因为mx是此次操作的最长回文子串的最右端。
在这里插入图片描述
然后对于i<mx的情况就稍微有点不太好理解了,参考着上图我讲一下这种情况。这是一个以id(上一次操作的位置)为中点长度为mx-my的回文串,j是以id为中点的i的对称点,因为j在之前遍历的时候就已经操作过了,所以Len[j]是已知的又因为回文串的性质可以知道Len[j]<=Len[i]的,my是mx的对称点。对于i<mx的情况还要再分情况,就是Len[j]和mx-i比较了,上图的情况是Len[j]是小于mx-i的,表示j位置的最长回文子串的长度是在id位置的最长回文子串的范围内的,还有一种情况就是Len[j]>mx-i,如下图所示,Len[j]的长度的范围超出了my。
在这里插入图片描述
所以我们需要对这两种情况再讨论一下,当Len[j] < mx-i的时候,表示Len[i]的长度可能不会超过mx-i,所以我们就从i的Len[2*id - i]也就是Len[mx-i]的地方开始匹配。当Len[j] > mx - i的时候,说明i位置的子串长度超过了mx,但mx以外的地方还没有遍历到,所以我们就从mx-i也就是mx的位置开始对i匹配。
然而最后的结果就是下图这样的
在这里插入图片描述
用一个sum去更新最大值就行了。我觉得挺不好理解的,建议耐下心想一想,模拟一遍,有不懂的都可以评论,有不对的地方也请指出。然后上一个前面说的那道题的代码,也是Manacher的模板。

#include <iostream>
#include <cstdio>
#include <cstring>
#define Min(a,b) a>b?b:a
#define Max(a,b) a>b?a:b
using namespace std;
const int N = 2e6 + 10 ;
int Len[N] , a[N] ;
char str[N] ;
int n,mx,id,len;
string s ;
void init(int l , int r){int k=0;str[k++] = '$';for(int i=l;i<=r;i++){str[k++]='#';str[k++]=s[i];}str[k++]='#';len=k;str[k] = '\0' ;
}
int ans = 0 , flag ;
int Manacher(){Len[0] = 0;int sum = 0;mx = 0;id = 0 ;// cout << str << endl ;for(int i=1;i<len;i++){if(i < mx) Len[i] = Min(mx - i, Len[2 * id - i]);else Len[i] = 1;while(str[i - Len[i]]== str[i + Len[i]]) Len[i]++;if(Len[i] + i > mx){        // 更新最长的回文串mx = Len[i] + i;          // mx是回文串右边一个位置id = i;             //id是回文串的中心sum = Max(sum, Len[i]);         // sum 是回文串的长度 + 1}// cout << i << " " << Len[i] << endl ;// if(Len[i] == i)  表示前缀是回文的//  {//    if(ans < Len[i])//     ans = Len[i] - 1 , flag = 1 ;//  }// if(Len[i] + i == len)  表示后缀是回文的//  if(ans < Len[i])//   ans = Len[i] - 1 , flag = 2 ;}return sum - 1 ;
}int main()
{scanf("%d",&n);while(n--){cin >> s ;len = s.size();int l = 0 , r = len - 1 ;init(l , r);ans = 0 , flag = 0 ;// cout << Manacher() << endl ;}return 0;
}

这篇关于马拉车算法模板的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/646735

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第