马拉车算法(C/C++)

news/2024/10/23 10:11:54/

#1024程序员节 | 征文#

马拉车算法(Manacher's Algorithm)是一种用于在字符串中查找最长回文子串的线性时间复杂度算法。该算法由Udi Manacher在1980年代提出,因此得名。它的核心思想是利用已知的回文信息来减少不必要的比较,从而提高效率。

算法步骤

  1. 预处理字符串: 为了处理奇数长度和偶数长度的回文串,算法首先对输入字符串进行预处理。在每个字符之间插入一个特殊字符(通常是#),并在字符串的开头和结尾也各插入一个。这样做的目的是为了让每个字符都有一个唯一的中心位置。

  2. 初始化变量

    • p[]:一个数组,用于存储每个位置的回文半径。
    • C:当前考虑的中心位置。
    • R:当前考虑的右端点,即以C为中心的回文串的最右端。
  3. 遍历字符串: 对于预处理后的字符串中的每个位置i算法尝试找到以i为中心的回文串的半径。这个过程分为两步:

    • 利用已知的回文信息:如果i在当前考虑的回文串内(即i < R),则可以利用对称位置的回文半径来减少比较次数。
    • 扩展回文串:如果i不在当前考虑的回文串内,或者需要进一步扩展回文串,算法会尝试扩展以i为中心的回文串,直到无法进一步扩展。
  4. 更新最大回文串: 在遍历过程中,如果找到的回文串半径大于之前记录的最大半径,则更新最大回文串的起始位置和半径。

  5. 提取最长回文子串: 遍历完成后,根据记录的最大半径和起始位置,从原始字符串中提取最长的回文子串。


算法详解

  • 中心扩展思想:马拉车算法利用了中心扩展的思想,即以每个字符为中心,向两边扩展,直到无法形成回文串为止。这种方法可以避免重复比较相同的字符对。

  • 利用对称性算法利用了回文串的对称性,即如果以i为中心的回文串已知,那么以2*C - i为中心的回文串的半径至少与以C为中心的回文串的半径相同。这里C是当前考虑的中心位置。

  • 动态规划:虽然马拉车算法不是传统意义上的动态规划算法,但它的思想与动态规划类似,即利用之前计算的结果来简化当前的计算。


复杂度分析

  • 时间复杂度:O(n),其中n是输入字符串的长度。这是因为每个位置的回文半径只计算一次。
  • 空间复杂度:O(n),用于存储回文半径的数组。

代码实现:

#include <iostream>
#include <vector>
#include <string>
using namespace std;string manacher(const string& s) {if (s.empty()) return s;// 预处理字符串,插入特殊字符string t = "#";for (char c : s) {t += c;t += "#";}int n = t.length();vector<int> p(n, 0);int C = 0, R = 0; // C为当前回文的中心,R为当前回文的右边界int maxLen = 0, centerIndex = 0;for (int i = 1; i < n; i++) {if (i < R) {int mirror = 2 * C - i;p[i] = min(R - i, p[mirror]);}int left = i - p[i], right = i + p[i];while (left >= 1 && right < n - 1 && t[left - 1] == t[right + 1]) {p[i]++;left--;right++;}if (i + p[i] > R) {C = i;R = i + p[i];}if (p[i] > maxLen) {maxLen = p[i];centerIndex = i;}}// 从预处理的字符串中提取最长回文子串int start = (centerIndex - maxLen) / 2;return s.substr(start, maxLen - 1); // 减1是因为预处理字符串中插入了特殊字符
}int main() {string s;cin >> s;cout << manacher(s) << endl;return 0;
}


应用实例:

在LeetCode上的题目“5. Longest Palindromic Substring”就是一个典型的应用实例,要求找出给定字符串中的最长回文子串。马拉车算法可以应用于生物信息学中DNA序列的回文结构分析,以及文本处理中的回文检测等场景。该算法也可以用于解决其他与回文相关的问题,如找出所有回文子串、判断回文对等。

算法实现部分:
string manacher(string s) {string t = "#";for (char c : s) {t += c;t += "#";}vector<int> p(t.size(), 0);int C = 0, R = 0;int maxLen = 0, centerIndex = 0;for (int i = 1; i < t.size(); i++) {if (i < R) p[i] = min(p[2 * C - i], R - i);else p[i] = 1;while (t[i + p[i]] == t[i - p[i]]) p[i]++;if (i + p[i] > R) {C = i;R = i + p[i];}if (p[i] > maxLen) {maxLen = p[i];centerIndex = i;}}int start = (centerIndex - maxLen) / 2;return s.substr(start, maxLen - 1);
}

马拉车算法是解决最长回文子串问题的有效方法,其线性时间复杂度使其在处理大规模数据时具有明显优势。文章到此为止感谢大家的支持。


http://www.ppmy.cn/news/1541294.html

相关文章

Java | Leetcode Java题解之第502题IPO

题目&#xff1a; 题解&#xff1a; class Solution {public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) {int n profits.length;int curr 0;int[][] arr new int[n][2];for (int i 0; i < n; i) {arr[i][0] capital[i];arr[i][1] profi…

Android14 和android12 在锁屏界面Keyguard输错5次密码后倒计时30秒时重启手机不显示倒计时

参考如下修改&#xff1a;Android9.0在锁屏界面Keyguard输错5次密码后倒计时30秒时重启手机不显示倒计时_android 锁屏密码输错5次-CSDN博客 android 14 修改如下&#xff1a; androidap/SYSTEM/frameworks/base$ git status Refresh index: 100% (47218/47218), done. HEAD d…

自定义组件使用v-model 实现双向数据绑定

在 Vue.js 中&#xff0c;如果你想在一个自定义组件中使用 v-model 来实现双向数据绑定&#xff0c;你需要遵循一些特定的步骤。v-model 实际上是以下两个属性的语法糖&#xff1a; 一个名为 value 的 prop&#xff0c;用于接收父组件传递的数据。一个名为 input 的事件&#…

Python流程控制专题:while、break与continue

在Python编程中,流程控制是至关重要的一个环节,能够让程序根据条件的不同而采取不同的执行路径。这篇博文将深入探讨Python中的三种主要流程控制结构:while循环,以及break和continue语句。我们将通过详细的解释、示例代码及应用场景,让你全面了解如何有效地使用这些控制结…

持续深化信创布局,途普科技与统信软件完成产品兼容性互认证

近日&#xff0c;由北京途普科技有限公司&#xff08;以下简称“途普科技”&#xff09;自主研发的TopGraph图数据库及知识图谱构建平台已成功完成统信服务器操作系统V20的兼容性互认证&#xff0c;标志着途普科技在国产自控技术上又迈出了坚实的一步。 在各项严格的测试环节中…

php中的错误和异常捕获

目录 一&#xff1a; 异常&#xff08;Exceptions&#xff09; 二&#xff1a; 错误&#xff08;Errors&#xff09; 三&#xff1a;实际项目的异常和错误处理 在PHP中&#xff0c;异常&#xff08;Exceptions&#xff09;和错误&#xff08;Errors&#xff09;是两个不同的…

Python画笔案例-083 绘制 3D世界坐标轴

1、绘制 3D世界坐标轴 通过 python 的turtle 库绘制 3D世界坐标轴,如下图: 2、实现代码 绘制 3D世界坐标轴,以下为实现代码: """3D世界坐标轴.py3D世界的每一个点,最终都是在屏幕显示出来,而屏幕是2D的。所以这个3D点就需要转换成2D坐标点。 ""…

前端工具类大全--【成果版】

目录 &#x1f4da;前言 如何判断Dom节点 Object.keys Object.assign Object.create 判断Number类型 判断String类型 判断Function类型 判断Object类型 判断Array类型 判断RegExp类型 遍历forEach 遍历map indexOf &#x1f4da;前言 前端最苦恼的问题之一…