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

LeetCode HOT100 - 回文子串

想到使用 manacher

先放代码,板子来自蒋老师,https://www.cnblogs.com/WIDA/p/17633758.html#马拉车manacher

std::vector<int> manacher(std::string s) {std::string t = "#";for (auto c : s) {t += c;t += '#';}int n = t.size();std::vector<int> r(n);for (int i = 0, j = 0; i < n; i++) {if (2 * j - i >= 0 && j + r[j] > i) {r[i] = std::min(r[2 * j - i], j + r[j] - i);}while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) {r[i] += 1;}if (i + r[i] > j + r[j]) {j = i;}}return r;
}

首先核心要求的就是 r[i] ,表示在变换后的串 t 中,以 i 为中心,最长能扩展出的回文半径

板子的开头是对字符串进行处理

主循环中,i 是当前要求的中心,j 是“当前最优中心”,即 它对应的回文向右延伸得最远

        while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) {r[i] += 1;}

这部分是好理解的,就是逐个判断当前中心能不能继续往外延展了

关键是

if (2 * j - i >= 0 && j + r[j] > i) {r[i] = std::min(r[2 * j - i], j + r[j] - i);
}

这也是保证复杂度的核心部分

2 * j - i 是指 i 关于 j 的镜像点,之所以要这个点是因为如果 i 落在以 j 为中心的那个大回文内部,那么根据回文对称性:i 附近的一部分回文信息,可以直接从 mirror 那里得到,进而可以少计算一些信息

j + r[j] 就是 j 对应的大回文右边界,因为当前点 i 落在以 j 为中心的那个“已知最右回文”内部才能像上面说的那样借用已有的信息

更新时就是对应点的半径信息和右边界到 i 距离的较小者

这样赋了初值后再暴力更新

最后再判断最优边界在这轮后有没有被刷新

对于这道题,使用板子的时候,就是要从 r[i] 出发求原串最长回文子串长度
在这种插 # 的写法下:原串中的最长回文长度 = max(r[i] - 1)

因为:

  • 在变换串中半径包含了分隔符
  • 去掉这些影响后,原串长度正好是 r[i] - 1

例如:

  • "aba" 的中心半径 r = 4(变换后是 "#a#b#a#"
  • 原串回文长度就是 4 - 1 = 3(注意是从半径到长度)
  • "abba" 变换后对应中心也会有 r = 5
  • 原串长度就是 5 - 1 = 4
int ans = 0;
for (int i = 0; i < r.size(); i++) {ans = std::max(ans, r[i] - 1);
}

于是完整代码就是

std::vector<int> manacher(std::string s) {std::string t = "#";for (auto c : s) {t += c;t += '#';}int n = t.size();std::vector<int> r(n);for (int i = 0, j = 0; i < n; i++) {if (2 * j - i >= 0 && j + r[j] > i) {r[i] = std::min(r[2 * j - i], j + r[j] - i);}while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) {r[i] += 1;}if (i + r[i] > j + r[j]) {j = i;}}return r;
}class Solution {
public:int countSubstrings(string s) {auto r = manacher(s);int ans = 0;for (int i = 0; i < r.size(); i++) {ans += max(0, (r[i] - 1 + 1) / 2);}return ans;}
};
http://www.jsqmd.com/news/519365/

相关文章:

  • Matlab基于连续小波变换(CWT)批量生成时频图
  • 从经纬度到平面坐标:ArcGIS中高斯投影的完整工作流(含自定义中央子午线技巧)
  • 洛谷 P1336:最佳课题选择 ← 分组背包
  • 最长公共子序列(LCS)——从零开始的动态规划
  • 学习web第三天
  • 深入解析DRAM:从基础原理到现代应用
  • Hive实战:3种生成自增ID的保姆级教程(附row_number与UDF对比)
  • 《医学大数据与人工智能》第二周
  • 计算机毕业设计:基于Python的图书数据分析系统 Flask框架 可视化 爬虫 书籍 大数据 机器学习(建议收藏)✅
  • C++中的 lower_bound 和 upper_bound:一篇讲清楚
  • 基于FPGA的FOC电流环手动编写Verilog实现:高效、可读性强的源码与Simulink模...
  • 迷宫算法面试通关指南:华为真题详解+DFS/BFS最优解套路
  • SpringBoot实战:5分钟搞定SSE消息推送,告别轮询烦恼
  • 告别人工规则:用MAPPO+自适应环境生成器,手把手教你训练能应对未知障碍物的无人机协同追捕AI
  • 从摄像头到CAN总线:手把手梳理智驾域控制器的数据接口与布线实战
  • 2026年 上海苏州OPC园区租赁招商推荐榜:精选优质产业园区,解析区位优势与服务体系,助力企业高效选址 - 品牌企业推荐师(官方)
  • LangGraph实战:构建具备状态与决策能力的智能体工作流
  • 计算机毕业设计:Python豆瓣图书数据分析平台 Flask框架 可视化 爬虫 书籍 大数据 机器学习(建议收藏)✅
  • 保姆级教程:用trackeval评估dancetrack多目标跟踪结果(附完整文件结构解析)
  • Codeforces Round 2209
  • UI 界面组成,控制界面代码
  • 【面试真题拆解】Java的Static关键字到底怎么用?
  • 3月18日笔记
  • Cookie操作避坑指南:从浏览器复制到Python requests的完整流程解析
  • 保姆级教程:用OpenWRT打造企业级访客WiFi(含防火墙规则+DHCP避坑指南)
  • Xilinx MMCM动态相位调整:从原理到实战的时钟微调指南
  • 信息学奥赛必备:5分钟搞定配对碱基链的两种C++解法(附完整代码)
  • 从PID到深度学习:柔性机器人控制算法演进全解析(附Python示例代码)
  • 从键盘到显示屏:给STM32F4计算器加个OLED界面(I2C驱动教程)
  • 揭示提示工程架构师创新实验室的神秘面纱