【字符串题目讲解】一文理解 Manacher Algoirth(马拉车算法)——以洛谷 P3805 和 P5446 为例

M a n a c h e r A l g o r i t h m \mathrm{Manacher\ Algorithm} Manacher Algorithm

Manacher 算法主要是解决怎样的问题呢,其实是求解最长的回文串,但是只能找到长度为奇数的回文串,不过可以通过转化使得能够求解任意长度的回文串。

例题:P3805 【模板】manacher

A l g o r i t h m F l o w \mathrm{Algorithm\ Flow} Algorithm Flow

Manacher 的算法是在 O ( n ) O(n) O(n) 的时间复杂度内,查找对于每一个 i i i,最大的回文半径 p i p_i pi 是多少?

从左往右递推做,在递推的过程中,每一次维护右端点最靠右的回文串的中心位置 m i d mid mid,以及该回文串最靠右的位置 m r mr mr(通常不包含该回文串最靠右的位置,即为该位置 + 1 +1 +1)。(如图)

那么,对于求解 i i i p i p_i pi,可以分情况讨论:

  • i i i m r mr mr 的左边,即在 m i d mid mid 所在回文串的内部。那么,充分利用已经算过的信息,考虑 i i i 在该回文串内的对应点 j j j,其中 j = m i d × 2 − i j=mid\times 2 - i j=mid×2i
  • j j j 所在的最大回文串仍然在 m i d mid mid 所在回文串的范围内,则 p i ≥ p j p_i\ge p_j pipj。(何时取 ≥ \ge ,当且仅当 j j j 的左端点恰好为 m i d mid mid 所在回文串的左端点时)
  • j j j 所在的最大回文串超出了 m i d mid mid 所在回文串的范围,则 p i = m r − i p_i=mr-i pi=mri

综上所述, p i ≥ min ⁡ ( p j , m r − i ) p_i\ge \min(p_j,mr-i) pimin(pj,mri)

  • i i i m r mr mr 的右边,即在 m i d mid mid 所在回文串的外部。那么,此时 p i ≥ 1 p_i\ge 1 pi1

最后,暴力的向外扩展即可:

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

判断当前的 p i + i p_i+i pi+i 是否超出 m r mr mr,若超出,则更新 m r mr mr m i d mid mid 即可。

A n a l y s i s o f t h e T i m e C o m p l e x i t y \mathrm{Analysis\ of\ the\ Time\ Complexity} Analysis of the Time Complexity

其实,时间复杂度就是 while 循环的执行次数(即暴力扩展的次数)。

只要执行 while 那么一定会更新 m r mr mr,且 while 执行的次数与 m r mr mr 向右扩展的数量相同。

所以 m r mr mr 总是线性向右递推的,所以总的时间复杂度为 O ( n ) O(n) O(n)

T r a n s f o r m \mathrm{Transform} Transform

刚才的算法只是解决一个字符串中奇数长度的回文串,偶数的是找不出来的。

不过,通过加一些字符可以转化为能够求出任意长度的最长回文串,具体如下:

在任意 2 2 2 个字符之间添加 1 1 1#,首尾也要加入 #,再在首尾分别加入 $^ 以防出界,因为你会发现 while 循环是没有边界的。

具体形式: $ # a 1 # a 2 # a 3 # … a n # ∧ \$ \# a_1\# a_2\# a_3\#\dots a_n\#\wedge $#a1#a2#a3#an#

这样对于新串的最长回文半径 − 1 -1 1 即为旧串最长回文串长度。

A c c e p t e d C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long longusing namespace std;typedef pair<int, int> PII;
typedef long long LL;const int SIZE = 4e7 + 10;int N;
string A, S;
int P[SIZE];void Manacher()
{int mid, mr = 1;for (int i = 1; i <= N; i ++){if (i < mr) P[i] = min(P[mid * 2 - i], mr - i);else P[i] = 1;while (S[i - P[i]] == S[i + P[i]]) P[i] ++;if (i + P[i] > mr){mr = i + P[i];mid = i;}}
}signed main()
{cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);cin >> A;N = A.size(), A = ' ' + A;S += "$#";for (int i = 1; i <= N; i ++)S += A[i], S += '#';S += '^';N = S.size(), S = ' ' + S;Manacher();int Result = 0;for (int i = 1; i <= N; i ++)Result = max(Result, P[i] - 1);cout << Result << endl;return 0;
}

练习题:P5446 【THUPC2018】绿绿和串串

D e s c r i p t i o n \mathrm{Description} Description

定义翻转的操作:把一个串以最后一个字符作对称轴进行翻转复制。形式化地描述就是,如果他翻转的串为 R R R,那么他会将前 R − 1 R-1 R1 个字符倒序排列后,插入到串的最后

现给出字符串 S S S,询问有哪些串长度不超过 ∣ S ∣ |S| S 的串 R R R 经过若干次翻转操作后得到的串作为 T T T 的前缀。

数据范围: ∑ ∣ S ∣ ≤ 5 × 1 0 6 \sum|S|\le 5\times 10^6 S5×106

S o l u t i o n \mathrm{Solution} Solution

考虑怎样的串翻转若干次之后前缀包含 S S S,其一定是 S S S 的一个前缀,这样翻转后 S S S 才会是它的前缀。

那么,对于 S S S 中每一个位置 i i i 分情况讨论:

  • 1 ∼ i 1\sim i 1i 的串翻转 1 1 1 次之后长度超过 ∣ S ∣ |S| S,则要求 i + 1 ∼ ∣ S ∣ i+1\sim |S| i+1S 的串是 1 ∼ i − 1 1\sim i-1 1i1 的串取反后的前缀,即以 i i i 为中心的回文串最大右端点为 ∣ S ∣ |S| S。标记 i i i 位置为可行位置。
  • 1 ∼ i 1\sim i 1i 的串翻转 1 1 1 次之后长度小于 ∣ S ∣ |S| S,则要求 1 ∼ 2 i − 1 1\sim 2i-1 12i1 是回文串,且右端点为可行位置,也就是能够经过多次翻转使 S S S 为其的前缀。

A c c e p t e d C o d e \mathrm{Accepted\ Code} Accepted Code

#include <bits/stdc++.h>
#define int long longusing namespace std;typedef pair<int, int> PII;
typedef long long LL;const int SIZE = 2e6 + 10;int N;
string S;
int P[SIZE], Vis[SIZE];void Manacher()
{int mr = 1, mid;for (int i = 1; i <= N; i ++){if (i < mr) P[i] = min(P[mid * 2 - i], mr - i);else P[i] = 1;while (S[i - P[i]] == S[i + P[i]]) P[i] ++;if (P[i] + i > mr)mr = P[i] + i, mid = i;}
}void solve()
{cin >> S;N = S.size(), S = ' ' + S, S += '^';Manacher();for (int i = N; i >= 1; i --)if (i + P[i] - 1 >= N) Vis[i] = 1;else if (Vis[i + P[i] - 1] && i - P[i] + 1 == 1) Vis[i] = 1;for (int i = 1; i <= N; i ++)if (Vis[i])cout << i << " ", Vis[i] = 0;cout << endl;
}signed main()
{cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);int Data;cin >> Data;while (Data --)solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/692431.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用智能电销机器人,拓客效果更佳!

现在很多的企业做销售都离不开电话营销&#xff0c;它是一种能够直接帮助企业获取更多利润的营销模式&#xff0c;目前被各大行业所采用。 znyx222 了解探讨 电话营销是一个压力很大的职业&#xff0c;新员工培养难度大、老员工又不好维护&#xff0c;会有情绪问题出现等&…

blasterswap明牌空投

空投要点 明牌空投&#xff0c;blaster生态第一个swap&#xff0c;应该不会寒酸交互简单&#xff0c;仅需3步&#xff0c;零gas费仅仅要求加密钱包在eth链有过交易需要有x和discord账号 blasterswap空投简介 BlasterSwap 是Blast生态里面第一个SWAP项目&#xff0c;近期启动…

git高级命令

取消上一次的commit命令&#xff1a; git reset --soft HEAD^ 查看提交记录&#xff1a; git log fq退出 回退指定的提交&#xff08;创建新的分支来回退&#xff09; git revert commit hash :wq退出 回退到指定的提交&#xff08;会修改你的分支历史&#xff09; git …

【嵌入式学习】QT-Day1-Qt基础

笔记 https://lingjun.life/wiki/EmbeddedNote/20QT 毛玻璃登录界面实现&#xff1a;

Fisher-Yates乱序算法

乱序算法 public class Test07 {public static void main(String[] args) {//乱序算法int[] arr {1,2,3,4,5,6,7,8};//逆序遍历 且这个随机的下标不能使要交换的元素的本身for(int i arr.length-1;i>0;i--){//产生一个随机的下标与当前元素进行交换int index (int)(Math…

智慧公厕管理系统:让城市智慧驿站更加智慧舒适

智慧公厕管理系统是城市智慧驿站中不可或缺的一部分&#xff0c;它通过全方位的信息化解决方案&#xff0c;为公共厕所的使用、运营和管理提供了一种智能化的方式。作为城市智慧驿站的重要组成部分&#xff0c;智慧公厕管理系统发挥着重要的作用&#xff0c;为城市社会民生提供…

记录 | git回退操作

修改了本地的代码&#xff0c;然后使用&#xff1a; git add file git commit -m 修改原因 执行commit后&#xff0c;还没执行push时&#xff0c;想要撤销这次的commit&#xff0c;该怎么办&#xff1f; 解决方案&#xff1a; 使用命令&#xff1a; git reset --soft HEAD^…

2024年华为OD机试真题-机器人仓库搬砖-Java-OD统一考试(C卷)

题目描述: 机器人搬砖,一共有N堆砖存放在N个不同的仓库中,第i堆砖中有bricks[i]块砖头,要求在8小时内搬完。机器人每小时能搬砖的数量取决于有多少能量格,机器人一个小时中只能在一个仓库中搬砖,机器人的能量格每小时补充一次且能量格只在这一个小时有效,为使得机器人损…

深入解析C和C++中的static关键字

C和C中的"static"关键字是一个非常强大且多用途的特性&#xff0c;它在内存分配、变量生命周期、函数行为等方面有着多种用途。在这篇博客中&#xff0c;我们将详细探讨"static"关键字在C和C中的使用方法和性能影响&#xff0c;并提供一些实际示例来帮助读…

RuntimeError: CUDA out of memory.【多种场景下的解决方案】

RuntimeError: CUDA out of memory.【多种场景下的解决方案】 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;【Matplotlib之旅&#xff1a;零基础精通数据可视化】 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于深度学…

SQL Developer 小贴士:显示Trace文件

SQL Developer可以识别trace文件&#xff0c;而无需利用tkprof进行转换。 在数据库服务器上生产trace文件。例如&#xff1a; alter session set tracefile_identifierdemo01_02;alter session set sql_tracetrue;-- your SQL here, for example select * from hr.employees;a…

防御第六次作业-防火墙综合实验(av、url过滤、dns过滤)

目录 拓扑图&#xff1a; 要求&#xff1a; 8 9 10 11 拓扑图 要求 前7个要求在上一篇博客&#xff1b; 8.分公司内部的客户端可以通过域名访问到内部的服务器 9.假设内网用户需要通过外网的web服务器和pop3邮件服务器下载文件和邮件&#xff0c;内网的FTP服务器也需要…

【Redis高手修炼之路】④主从复制

主从复制 就是 redis集群的策略配从&#xff08;库&#xff09;不配主&#xff08;库&#xff09;&#xff1a;小弟可以选择谁是大哥&#xff0c;但大哥没有权利去选择小弟读写分离&#xff1a;主机写&#xff0c;从机读 一主二仆 准备三台服务器&#xff0c;并修改redis.co…

【复现】Wuzhi cms后台sql注入漏洞_53

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议:

Linux系统调用、程序执行相关命令(待补充)

在Linux系统中&#xff0c;有多种工具可以用于跟踪系统调用、程序执行、性能分析等。这里列出一些常用的跟踪工具&#xff1a; strace: 用于跟踪进程执行时的系统调用和接收到的信号。它对于理解程序如何与Linux内核交互非常有用。 使用示例&#xff1a; strace lsltrace: 类似…

Linux归档命令cpio

cpio 是一种在 Unix 和类 Unix 系统&#xff08;如 Linux&#xff09;上用于创建和提取归档文件的工具。与 tar 命令类似&#xff0c;cpio 可以用来打包和解包文件和目录&#xff0c;但它在某些方面提供了不同的功能和选项。cpio 命令通常与 find 命令结合使用&#xff0c;以创…

Spin Image特征描述子简介

一、向量点积 二、狄拉克δ函数公式 三、Spin Image特征描述子原理 Spin Image自旋图像描述符可视化以及ICP配准-CSDN博客

C++结合Lambda表达式在函数内部实现递归

529. 扫雷游戏 已解答 中等 相关标签 相关企业 让我们一起来玩扫雷游戏&#xff01; 给你一个大小为 m x n 二维字符矩阵 board &#xff0c;表示扫雷游戏的盘面&#xff0c;其中&#xff1a; M 代表一个 未挖出的 地雷&#xff0c;E 代表一个 未挖出的 空方块&#xff…

【XR806开发板试用】+移植rosserial到XR806

1 XR806简介 板子来源于极术社区的试用&#xff0c;XR806的在线网址 其主要参数&#xff1a; 主控XR806AF2LDDRSIP 288KB SRAM存储SIP 160KB Code ROM. SIP 16Mbit Flash.天线板载WiFi/BT双天线&#xff0c;可共存按键reboot按键 1&#xff0c;功能按键 1灯红色电源指示灯 1…

如何使用阿里云OSS进行前端直传

在使用阿里云OSS进行前端直传时&#xff0c;首先我们需要去阿里云官网注册自己的存储桶&#xff0c;然后申请相关的accessKeyId和accessKeySecret&#xff0c;然后新建一个桶&#xff0c;为这个桶命名以及选择对应的地区。 然后可以根据自己的业务&#xff0c;封装对应的组件&a…