详解一下马拉车算法 Manache算法 使用c++

马拉车算法是寻找最长回文子串的高效算法,时间复杂度为O(n)

#include <iostream>
#include <string>
#include <vector>
using namespace std;string longestPalindrome(string s) {// 步骤1: 预处理,在字符间插入特殊字符'#'string t = "#";for (char c : s) {t += c;t += "#";}// 步骤2: 初始化变量int n = t.length();vector<int> P(n, 0);  // P[i]存储以i为中心的回文半径int C = 0;  // 当前回文串的中心int R = 0;  // 当前回文串的右边界int maxLen = 0;  // 最长回文子串的长度int centerIndex = 0;  // 最长回文子串的中心位置// 步骤3: 主循环,计算每个位置的回文半径for (int i = 0; i < n; i++) {// 确定初始回文半径if (i < R) {int mirror = 2 * C - i;  // i关于C的对称点P[i] = min(R - i, P[mirror]);  // 利用对称性}// 尝试扩展回文int a = i + (1 + P[i]);int b = i - (1 + P[i]);while (a < n && b >= 0 && t[a] == t[b]) {P[i]++;a++;b--;}// 更新C和Rif (i + P[i] > R) {C = i;R = i + P[i];}// 更新最长回文子串信息if (P[i] > maxLen) {maxLen = P[i];centerIndex = i;}}// 步骤4: 提取最长回文子串int start = (centerIndex - maxLen) / 2;return s.substr(start, maxLen);
}int main() {string s = "babad";cout << "输入字符串: " << s << endl;cout << "最长回文子串: " << longestPalindrome(s) << endl;return 0;
}

1.首先要进行预处理在每个字符之间添加"#",这样就可以统一处理奇数和偶数的字符串

2.变量有,回文半径组p,回文中心c,右边界r.

Manacher算法的核心思想是利用已经计算出的回文信息来避免重复计算。通过维护当前最右的回文边界,算法可以在大多数情况下直接得到一个很好的初始回文半径.

为了体现高继承性,下面我多使用了类来解答马拉车算法

#include <iostream>
#include <string>
#include <vector>class Manacher {
private:std::string original;  // 原始字符串std::string processed; // 预处理后的字符串std::vector<int> palindromeLengths; // 回文长度数组int maxPalindromeLength; // 最长回文子串的长度int maxPalindromeCenter; // 最长回文子串的中心位置// 预处理字符串,在字符间插入特殊字符'#'void preprocess() {processed = "#";for (char c : original) {processed += c;processed += "#";}}// 核心Manacher算法void computePalindromeLengths() {int n = processed.length();palindromeLengths.resize(n, 0);int center = 0;  // 当前回文串的中心int right = 0;   // 当前回文串的右边界for (int i = 0; i < n; i++) {// 利用对称性初始化回文长度if (i < right) {int mirror = 2 * center - i;palindromeLengths[i] = std::min(right - i, palindromeLengths[mirror]);}// 尝试扩展回文int a = i + (1 + palindromeLengths[i]);int b = i - (1 + palindromeLengths[i]);while (a < n && b >= 0 && processed[a] == processed[b]) {palindromeLengths[i]++;a++;b--;}// 更新中心和右边界if (i + palindromeLengths[i] > right) {center = i;right = i + palindromeLengths[i];}// 更新最长回文子串信息if (palindromeLengths[i] > maxPalindromeLength) {maxPalindromeLength = palindromeLengths[i];maxPalindromeCenter = i;}}}public:// 构造函数Manacher(const std::string& s) : original(s), maxPalindromeLength(0), maxPalindromeCenter(0) {preprocess();computePalindromeLengths();}// 获取最长回文子串std::string getLongestPalindrome() {int start = (maxPalindromeCenter - maxPalindromeLength) / 2;return original.substr(start, maxPalindromeLength);}// 获取所有回文子串的数量int countAllPalindromes() {int count = 0;for (int length : palindromeLengths) {count += (length + 1) / 2;}return count;}// 判断子串是否为回文bool isPalindrome(int start, int end) {int len = end - start + 1;int center = start + end + 1; // 在processed字符串中的中心位置return palindromeLengths[center] >= len;}
};int main() {std::string s = "babad";Manacher manacher(s);std::cout << "输入字符串: " << s << std::endl;std::cout << "最长回文子串: " << manacher.getLongestPalindrome() << std::endl;std::cout << "回文子串的总数: " << manacher.countAllPalindromes() << std::endl;std::cout << "子串'bab'是否为回文: " << (manacher.isPalindrome(0, 2) ? "是" : "否") << std::endl;return 0;
}

最后让我详细解释一下为什么在预处理字符串中,每个回文长度对应原字符串中的 (length + 1) / 2 个不同回文。

  1. 预处理字符串的特性:
    • 在预处理后的字符串中,每个原始字符之间都插入了 '#'。
    • 这意味着预处理字符串的长度是原始字符串长度的两倍加一。
  2. 回文中心的两种情况:
    • 在原字符串中,回文中心可能在字符上,也可能在字符之间。
    • 在预处理字符串中,所有可能的回文中心都变成了字符位置(包括 '#')。
  3. 回文长度的对应关系:
    • 预处理字符串中长度为 1 的回文对应原字符串中长度为 1 的回文。
    • 预处理字符串中长度为 3 的回文对应原字符串中长度为 2 的回文。
    • 预处理字符串中长度为 5 的回文对应原字符串中长度为 3 的回文。
    • 以此类推...
  4. 计算公式的推导:
    • 设预处理字符串中的回文长度为 length。
    • 对应的原字符串回文长度为 (length + 1) / 2。
    • 例如:
      • length = 1 时,原字符串回文长度 = (1 + 1) / 2 = 1
      • length = 3 时,原字符串回文长度 = (3 + 1) / 2 = 2
      • length = 5 时,原字符串回文长度 = (5 + 1) / 2 = 3
  5. 为什么是不同的回文:
    • 在预处理字符串中,每个位置为中心的最长回文都是唯一的。
    • 这个最长回文包含了以该中心的所有较短回文。
  6. 举例说明: 考虑原字符串 "ababa" 和预处理字符串 "#a#b#a#b#a#":
    • 对于中心位置 5(原字符串中的中间 'a'):
      • 回文长度为 5,对应原字符串中 "ababa"
      • 回文长度为 3,对应原字符串中 "aba"
      • 回文长度为 1,对应原字符串中 "a"
    • 这三个回文都是不同的,且都以这个 'a' 为中心。
  7. 公式的应用:
    • 对于长度为 5 的回文,(5 + 1) / 2 = 3,正好对应这个中心位置的 3 个不同回文。

通过这种方式,(length + 1) / 2 既计算了原字符串中对应的回文长度,又恰好表示了以该位置为中心的不同回文数量。这个巧妙的关系使得我们可以快速计算所有回文子串的数量,而不需要逐一枚举。

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

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

相关文章

【Linux】进程控制的详细介绍

前言 在此之前&#xff0c;我们学过进程的概念&#xff0c;进程的状态&#xff0c;进程地址空间等一系列进程相关的问题。本章我们继续学习进程&#xff0c;我们要来学习一下进程的控制&#xff0c;关于进程等待&#xff0c;进程替换等问题。 目录 1.再次认识Fork函数1.1 fork…

internet download manager(IDM下载器) 6.42.8.2下载安装使用指南

internet download manager(IDM下载器) 6.42.8.2Z是一款功能强大的下载加速工具&#xff0c;能够显著提升您的下载速度&#xff0c;最高可达500%。它不仅能够加速下载&#xff0c;还能对下载任务进行智能调度&#xff0c;并具备恢复中断下载的能力。根据用户评价&#xff0c;无…

初识C++(命名空间、缺省参数)

初识C 命名空间namespace关键字命名空间的使用 缺省参数 命名空间 namespace关键字 在C中&#xff0c;为了尽可能避免命名冲突&#xff0c;需要对各个变量进行域作用限定&#xff0c;这就需要使用到namespace关键字&#xff0c;namespace可以定义一个命名空间&#xff0c;即命…

LabVIEW红外热波图像缺陷检

开发使用LabVIEW开发的红外热波图像缺陷检测系统。该系统结合红外热像仪、工业相机和高效的数据采集硬件&#xff0c;实现对工件表面缺陷的自动检测和分析。通过LabVIEW的强大功能&#xff0c;系统能够实时采集、处理和显示红外热波图像&#xff0c;有效提高了检测的精度和效率…

vue:标签属性绑定Vue实例【ref,reactive,内置指令v-bind,v-on】,预定义变量、方法【$methods,$computed】

Vue2、3组件通信、双向绑定、插槽slot、内置指令_组件双向绑定-CSDN博客​Vue2&#xff0c;3响应式原理&#xff0c;ref和reactive&#xff0c;toRef和toRefs&#xff0c;shallowRef和shallowRefs_vue2 shallowref-CSDN博客 vue2【Options 选项API、mixin混入】&#xff0c;vu…

WAF基础介绍

WAF 一、WAF是什么&#xff1f;WAF能够做什么 二 waf的部署三、WAF的工作原理 一、WAF是什么&#xff1f; WAF的全称是&#xff08;Web Application Firewall&#xff09;即Web应用防火墙&#xff0c;简称WAF。 国际上公认的一种说法是&#xff1a;Web应用防火墙是通过执行一…

免开steam 脱离steam 进行游戏的小工具

链接&#xff1a;https://pan.baidu.com/s/1k2C8b4jEqKIGLtLZp8YCgA?pwd6666 提取码&#xff1a;6666 我们只需选择游戏根目录 然后输入AppID 点击底部按钮 进行就可以了 关于AppID在&#xff1a;

机器学习——L1 L2 范数 —>L1 L2正则化

1、L1范数和L2范数是机器学习和数据分析中经常使用的两种范数&#xff0c;它们之间存在多个方面的区别。 以下是关于L1范数和L2范数区别的详细解释&#xff1a; 一、定义差异 L1范数&#xff1a;也被称为曼哈顿范数&#xff0c;是向量元素的绝对值之和。对于一个n维向量x&am…

酒店管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;酒店管理员管理&#xff0c;房间类型管理&#xff0c;房间信息管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;房间信息…

Linux介绍与常用命令详解

目录 一、Linux概述 1.Linux发行版 2.Linux目录结构 二、Linux特点 三、Linux用途 四、Linux常用的命令 1.cd指令&#xff08;跳转位置&#xff09; 2.显示目录文件 3.对文件进行操作 4.rm指令&#xff08;删除文件夹指令&#xff09; 5.mv指令 6.查看文件命令 7.进程命令…

【云岚到家】-day05-6-项目迁移-门户-CMS

【云岚到家】-day05-6-项目迁移-门户-CMS 4 项目迁移-门户4.1 迁移目标4.2 能力基础4.2.1 缓存方案设计与应用能力4.2.2 静态化技术应用能力 4.3 需求分析4.3.1 界面原型 4.4 系统设计4.4.1 表设计4.4.2 接口与方案4.4.2.1 首页信息查询接口4.4.3.1 数据缓存方案4.4.3.2 页面静…

力扣678.有效的括号字符串

力扣678.有效的括号字符串 用两个栈分别存’ ( ‘和‘ * ’的下标 ‘ ) ’ 与二者匹配最后将‘ ( ’与 ‘ * ’匹配 class Solution {public:bool checkValidString(string s) {stack<int> st1,st2;int n s.size();for(int i0;i<n;i){char c s[i];if(c ()st1.pus…

宪法学学习笔记(个人向) Part.5

宪法学学习笔记(个人向) Part.5 4. 公民基本权利和义务 4.1 公民&#x1f338; 概念 是指具有某个国家国籍的自然人&#xff1b; 【拓展】国籍&#xff1a;在宪法上是指一个人隶属于某个国家的法律上的身份&#x1f338; &#xff1b; 取得方式 出生国籍 因出生而获得的国籍&a…

Ubuntu20.04 编译安装FFmpeg,出错分析以及解决方案

最近工程上需要对FFmpeg底层源码进行修改&#xff0c;需要重新编译&#xff0c;遇见不少坑&#xff0c;出篇教程记录一下。 文章目录 1.FFmpeg源码下载地址2.编译环境配置3.编译FFmpeg4.配置FFmpeg运行环境 1.FFmpeg源码下载地址 官方下载地址:Index of /releases (ffmpeg.or…

Java | Leetcode Java题解之第232题用栈实现队列

题目&#xff1a; 题解&#xff1a; class MyQueue {Deque<Integer> inStack;Deque<Integer> outStack;public MyQueue() {inStack new ArrayDeque<Integer>();outStack new ArrayDeque<Integer>();}public void push(int x) {inStack.push(x);}pub…

springboot1——快速构建项目

需求 第一步&#xff1a;创建maven工程(非web项目) 第二步&#xff1a;导入起步依赖 点击&#xff1a; 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xff01;这是springboot工程需要继承的父工程 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xf…

Python实现一对多WebSocket发送给指定多个客户端

在一对多的WebSocket场景下&#xff0c;如果你想要向特定的多个客户端发送消息&#xff0c;而不是广播给所有客户端&#xff0c;你需要维护一个能够标识每个客户端的方式&#xff0c;比如使用用户名或者客户端ID。这样&#xff0c;你就可以根据需要选择向哪些客户端发送消息。 …

Nodejs 第八十章(Kafka高级)

kafka前置知识在前几章章讲过了 不再复述 Kafka集群操作 1.创建多个kafka服务 拷贝一份kafka完整目录改名为kafka2 修改配置文件 kafka2/config/server.properties 这个文件 broker.id1 //唯一broker port9093 //切换端口 listenersPLAINTEXT://:9093 //切换监听源启动zooKe…

Rust编程-函数式编程

函数式编程&#xff1a; 函数式风格编程通常包括将函数当作参数、将函数作为其他函数的返回值或将函数赋给变量以备之后执行等。 闭包&#xff1a; 一个类似函数&#xff0c;并且可以存储在变量中的结构。闭包拥有很好的运行性能。 闭包可以存入变量或作为参数传递给其他函数的…

常见问题记录(持续更新)

备注&#xff1a; 在7月10日记录之前遇到的问题及解决方法: 一&#xff1a;常见的访问问题&#xff1a; 403 Forbidden&#xff1a;&#xff08;未有请求权限&#xff09; 表示服务器理解请求但是拒绝执行它。这通常是由于服务器上的文件或资源没有正确的读、写或执行权限&…