当前位置: 首页 > news >正文

manacher算法

Manacher 算法快速入门

Manacher 算法是一种用于寻找字符串中最长回文子串的高效算法,时间复杂度为 O(n)


基本概念

回文

回文是一个字符串,从左到右和从右到左读都一样。

示例

  • 回文:"aba""abba"
  • 非回文:"abc""abcd"

算法目标

给定字符串 s,找到其最长回文子串。

输入"babad"
输出"bab""aba"


算法思想

  1. 字符串预处理

    • 为了统一奇数和偶数长度的回文形式,插入分隔符 #
    • 例如:"babad""#b#a#b#a#d#"
  2. 维护变量

    • P[i]:记录以位置 i 为中心的回文半径。
    • C:当前回文的中心。
    • R:当前回文的右边界。
  3. 扩展回文并优化

    • 对每个字符尝试扩展,并利用对称性优化。
  4. 提取结果

    • 根据 P 数组找到最大值,从而还原最长回文子串。

详细步骤

Step 1: 字符串预处理

通过插入 #,将奇数和偶数回文统一。

原始字符串"babad"
预处理后"#b#a#b#a#d#"

Step 2: 遍历字符串并更新

初始化 P 数组,按以下规则遍历:

  1. 对称性优化
    • i 在右边界内:
      [
      P[i] = \min(P[\text{mirror}], R - i)
      ]
    • 对称点:mirror = 2C - i
  2. 尝试扩展
    • t[i + P[i] + 1] == t[i - P[i] - 1] 条件下,扩展 P[i]
  3. 更新中心和右边界
    • 如果扩展后的回文超出当前右边界,则更新 CR

Step 3: 提取最长回文

P 数组中找到最大值 P[i],其对应的中心点 i 即为最长回文的中心。

利用公式:
[
\text{start} = \frac{\text{中心索引} - \text{半径}}{2}
]
将索引映射回原字符串,提取子串。


其中最关键的是关于中心 C 这个对称点的理解

比如 #a#b#a#b#a#c#
#a#b#a 对应 为 0 1 0 3 0 5
这时候 因为 最后一个字符的半径最大 这时候 找下一个字符的时候, 中心点就是 长度为5 的这个a

之后后面查找的时候, 就可以知道, 这个半径内, 左右是一样的, 那就可以快速跳过重复的查找

在这里插入图片描述

在这里插入图片描述

C++ 实现代码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>using namespace std;string manacher(const string &s) {// 步骤 1: 预处理字符串,在每个字符两侧插入分隔符('#')string t = "#";for (char c : s) {t += c;t += "#";}int n = t.size();vector<int> P(n, 0); // 数组 P 存储以每个位置为中心的回文半径int C = 0, R = 0;    // 当前回文的中心 C 和右边界 R// 步骤 2: 遍历预处理后的字符串for (int i = 0; i < n; ++i) {// i 关于中心 C 的对称点int mirror = 2 * C - i;// 如果 i 在当前回文右边界内,初始化 P[i]if (i < R) {P[i] = min(P[mirror], R - i);}// 尝试扩展以 i 为中心的回文while (i + P[i] + 1 < n && i - P[i] - 1 >= 0 && t[i + P[i] + 1] == t[i - P[i] - 1]) {++P[i];}// 如果回文扩展超出当前右边界,更新中心 C 和右边界 Rif (i + P[i] > R) {C = i;R = i + P[i];}}// 步骤 3: 找到 P 数组中最大值,确定最长回文int max_len = 0, center_index = 0;for (int i = 0; i < n; ++i) {if (P[i] > max_len) {max_len = P[i];center_index = i;}}// 步骤 4: 从原始字符串中提取最长回文子串int start = (center_index - max_len) / 2; // 将索引从预处理后的字符串转换回原始字符串return s.substr(start, max_len);
}int run() {string s;cin >> s;string longest_palindrome = manacher(s);cout << "最长回文子串: " << longest_palindrome << endl;return 0;
}int main() {// 如果在苹果或Windows系统上,重定向输入输出
#if defined(__APPLE__) || defined(__WIN32__ )freopen("./slyar.in", "r+", stdin); // 重定向标准输入到slyar.in文件freopen("./slyar.out", "w+", stdout); // 重定向标准输出到slyar.out文件
#endifrun(); // 调用运行主逻辑函数
}
http://www.lryc.cn/news/513404.html

相关文章:

  • Cocos2dx Lua绑定生成中间文件时参数类型与源码类型不匹配
  • 为什么需要 std::call_once?
  • ubuntu非root用户操作root权限问题-virbox挂在共享文件夹
  • 网络通讯协议
  • centos,789使用mamba快速安装devtools
  • 【人工智能机器学习基础篇】——深入详解强化学习之常用算法Q-Learning与策略梯度,掌握智能体与环境的交互机制
  • 银河麒麟桌面v10sp1修复引导笔记
  • 深入理解 MVCC 与 BufferPool 缓存机制
  • vue实现下拉多选、可搜索、全选功能
  • 探秘Kafka源码:关键内容解析
  • Android音频效果处理:基于`android.media.audiofx`包的原理、架构与实现
  • LeetCode - 初级算法 数组(两个数组的交集 II)
  • SQL 实战:分页查询的多种方式对比与优化
  • 汇川Easy系列正弦信号发生器(ST源代码)
  • JavaSpring AI与阿里云通义大模型的集成使用Java Data Science Library(JDSL)进行数据处理
  • Three.js教程002:Three.js结合Vue进行开发
  • pycharm+anaconda创建项目
  • vue2中遇到的问题与解决方案(自用)
  • CF2043b-B. Digits
  • ultralytics库RT-DETR代码解析
  • (七)- plane/crtc/encoder/connector objects
  • 基于STM32的四轴飞行器的控制系统(论文+源码)
  • 混合精度训练(Mixed Precision Training)中为什么在训练过程中不直接使用bf16进行权重更新?中英双语
  • 【java】HashMap的实现原理
  • FCM32F103C8T6开发指引
  • Python世界:人生苦短,我用Python
  • 【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用
  • 从论文到实践:Stable Diffusion模型一键生成高质量AI绘画
  • 项目管理:用甘特图 “导航” 项目全程
  • v3.0.8- 「S+会员」新增专属运动秀,试试新穿搭吧- 与「好友」