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

回文侦探:三种境界破解最长回文子串

回文侦探:三种境界破解最长回文子串

LeetCode 5. 最长回文子串—— 从暴力到线性,看懂回文问题的三种解题境界。


一、故事开场:你是回文侦探

想象你接到一个任务:在一串字符里,找出最长的"镜像文字"

比如"babad"里,"bab""aba"都是回文 —— 正着读反着读都一样。

你的目标是:在所有回文子串中,找到最长的那个。

但字符串可能有 1000 个字符,肉眼扫描?不现实。

这就是 LeetCode 第 5 题 ——最长回文子串。它看似简单,却藏着三种截然不同的解法境界。


二、暴力解法:为什么不直接枚举?

最笨的方法:枚举所有子串,判断是不是回文。

  • 子串数量:O(n2)O(n^2)O(n2)
  • 判断回文:O(n)O(n)O(n)
  • 总时间:O(n3)O(n^3)O(n3)

面试官听了会摇头。我们需要更聪明的方法。


三、解法一:中心扩展法(面试首选)

核心思路

回文串有个特点:它有一个中心,两边对称。

中心有两种可能:

  • 1 个字符(奇数长度,如"aba"
  • 2 个字符(偶数长度,如"abba"

所以,遍历每个字符,分别作为两种中心,向两边扩展,直到不再对称为止。

下标: 0 1 2 3 4 字符: b a b a d ↑ i=1(中心) 扩展: 左=0, 右=2 → 'b'=='b' ✅ 左=-1, 右=3 → 越界,停! 得到回文: "bab",长度 3

代码实现

classSolution{publicStringlongestPalindrome(Strings){if(s==null||s.length()<1)return"";intstart=0,end=0;for(inti=0;i<s.length();i++){intlen1=expand(s,i,i);// 奇数中心intlen2=expand(s,i,i+1);// 偶数中心intlen=Math.max(len1,len2);if(len>end-start){start=i-(len-1)/2;end=i+len/2;}}returns.substring(start,end+1);}privateintexpand(Strings,intleft,intright){while(left>=0&&right<s.length()&&s.charAt(left)==s.charAt(right)){left--;right++;}returnright-left-1;// 实际回文长度}}

复杂度

  • 时间O(n2)O(n^2)O(n2)—— 每个中心最多扩展nnn
  • 空间O(1)O(1)O(1)—— 只记录左右边界

评价

就像一个侦探,从每个可能的中心点开始,向两边展开推理。虽然要查nnn个中心点,但每个案子都不复杂。


四、解法二:动态规划(理解回文的本质)

核心思路

中心扩展是"从中间向两边看",动态规划则是"从小回文推大回文"。

定义dp[i][j]子串s[i..j]是否为回文串。

状态转移:

  • s[i] != s[j]dp[i][j] = false(首尾不同,肯定不是)
  • s[i] == s[j]→ 看"去掉首尾后"是不是回文
    • 如果子串长度≤2\leq 22(如"a""aa")→ 直接为true
    • 否则dp[i][j] = dp[i+1][j-1]
s = "abba" dp[0][3]: s[0]=='a', s[3]=='a' → 看 dp[1][2] dp[1][2]: s[1]=='b', s[2]=='b' → 长度=2 → true 所以 dp[0][3] = true ✅

遍历顺序很关键:i必须从大到小(因为依赖i+1),j从小到大

代码实现

classSolution{publicStringlongestPalindrome(Strings){intn=s.length();if(n<2)returns;boolean[][]dp=newboolean[n][n];intstart=0,maxLen=1;for(inti=n-1;i>=0;i--){for(intj=i;j<n;j++){if(s.charAt(i)==s.charAt(j)){if(j-i<=2){dp[i][j]=true;}else{dp[i][j]=dp[i+1][j-1];}}if(dp[i][j]&&(j-i+1)>maxLen){maxLen=j-i+1;start=i;}}}returns.substring(start,start+maxLen);}}

复杂度

  • 时间O(n2)O(n^2)O(n2)
  • 空间O(n2)O(n^2)O(n2)—— 需要二维数组

评价

就像建立一个"回文档案库",把每个小片段的回文性质都记录下来。当你想知道一个大片段是不是回文时,只需要查档案,而不需要重新验证。


五、解法三:马拉车算法(Manacher)—— 线性时间的奇迹

核心思路

前面两种方法都是O(n2)O(n^2)O(n2),能不能更快?

马拉车算法做到了O(n)O(n)O(n)。它的核心思想是:利用回文的对称性,避免重复计算。

但直接处理奇偶长度很麻烦,所以第一步是预处理:在每个字符间插入#,把字符串变成统一奇数长度。

原串: a b b a 处理: # a # b # b # a # 下标: 0 1 2 3 4 5 6 7 8

现在所有回文都是奇数长度,中心只有一个。

算法维护一个"最右边界"right和对应的中心center。当处理位置i时:

  • 如果iright左边,它关于center的对称点mirror已经被算过了,可以直接利用
  • 但需要注意边界限制,不能直接照搬

代码实现

classSolution{publicStringlongestPalindrome(Strings){if(s==null||s.length()<1)return"";// 预处理:插入 #,统一奇偶StringBuildersb=newStringBuilder("#");for(charc:s.toCharArray()){sb.append(c).append("#");}Stringt=sb.toString();intn=t.length();int[]p=newint[n];// p[i] = 以 i 为中心的回文半径intcenter=0,right=0;// 当前最右回文的中心和右边界intmaxLen=0,start=0;// 记录最长回文for(inti=0;i<n;i++){// 1. 利用对称性初始化 p[i]intmirror=2*center-i;// i 关于 center 的对称点if(i<right){p[i]=Math.min(right-i,p[mirror]);}// 2. 尝试继续扩展intl=i-(p[i]+1);intr=i+(p[i]+1);while(l>=0&&r<n&&t.charAt(l)==t.charAt(r)){p[i]++;l--;r++;}// 3. 更新最右边界if(i+p[i]>right){center=i;right=i+p[i];}// 4. 记录最长回文(转回原串坐标)if(p[i]>maxLen){maxLen=p[i];start=(i-p[i])/2;}}returns.substring(start,start+maxLen);}}

复杂度

  • 时间O(n)O(n)O(n)—— 每个字符最多被访问常数次
  • 空间O(n)O(n)O(n)—— 预处理和半径数组

评价

就像一位老练的侦探,不会每次遇到相似线索都从头推理。他会建立"对称档案":发现 A 和 B 对称,A 的结论可以直接套用到 B 上。这是算法世界里最美的"偷懒"艺术。


六、三种境界对比

境界算法时间空间核心思想推荐指数
凡人暴力枚举O(n3)O(n^3)O(n3)O(1)O(1)O(1)枚举所有子串
高手中心扩展O(n2)O(n^2)O(n2)O(1)O(1)O(1)从中心向两边扩展⭐⭐⭐⭐⭐
大师动态规划O(n2)O(n^2)O(n2)O(n2)O(n^2)O(n2)小回文推大回文⭐⭐⭐⭐
神仙马拉车O(n)O(n)O(n)O(n)O(n)O(n)利用对称性避免重复计算⭐⭐⭐

选哪个?

  • 面试写代码→ 中心扩展法,代码短、空间优、不易出错
  • 理解回文本质→ 动态规划,是很多回文类题的基础
  • 追求极限性能→ 马拉车算法,但代码复杂,面试一般不考

七、写在最后

回文问题的美,在于它把"对称"这个直觉概念,变成了可以量化的算法。

从中心扩展的直观,到动态规划的系统,再到马拉车算法的优雅,三种解法像三种人生境界:

中心扩展是活在当下,动态规划是积累经验,马拉车则是站在经验的肩膀上飞翔。

但归根结底,最实用的往往是那个看起来"最笨"的中心扩展法 —— 因为它足够简单,足够可靠,就像生活中那些朴实却有效的道理。

简单,往往是最深的功力。


欢迎在评论区分享你的理解,或者指出我表述不清的地方。一起进步!

http://www.cnnetsun.cn/news/3806008.html

相关文章:

  • Claude Cowork重塑AI办公:从Copilot到协同工作的范式转移
  • QQ音乐解密终极指南:3分钟解锁加密音乐文件的完整教程
  • StarRailAssistant:崩坏星穹铁道自动化助手的完整使用指南
  • 终极Windows热键冲突检测指南:如何快速定位并解决快捷键占用问题
  • OpCore-Simplify:如何用智能工具在30分钟内完成黑苹果配置?
  • 【Bug已解决】FSDP2 fails due to KeyError: ‘lm_head.weight‘ 解决方案
  • 【Bug已解决】Degraded performance when resuming from checkpoint 解决方案
  • 【限时解密】头部券商内部使用的AI流失预警模型架构图首次公开:含3层动态阈值引擎与HR协同干预SOP
  • PyTorch入门指南:从环境搭建到自动求导的NLP学习实战
  • 我的智能Agent上线崩了,才明白权限日志比调API更重要
  • 理工科论文去 AI 味会把公式术语改乱吗?亲测一次降到 9% 术语没动
  • 鸣潮自动化解决方案深度解析:基于图像识别的智能游戏辅助架构剖析
  • 周末搓火锅找靠谱店,亲测4家新鲜现切的火锅店
  • OBS Studio色彩校正技术深度解析:从3D LUT到专业级色彩分级
  • Cyclone常见问题解答:新手开发者必知的15个要点
  • 如何永久保存微信聊天记录:3步实现数据自主掌控的终极方案
  • 如何快速下载国家中小学智慧教育平台电子课本PDF文件:完整指南
  • 终极指南:OpenCore Legacy Patcher完整教程,让老款Mac焕发新生
  • Python PDF处理终极指南:pypdf库从入门到精通
  • 三步解锁Windows预览HEIC照片的完整方案
  • 3D外壳设计全流程:从概念到量产,从CAD建模到3D打印实战
  • 从Karpathy内部Claude.md看AI交互工程化:构建可版本控制的提示词系统
  • 洛雪音乐音源终极指南:5分钟免费搭建高品质音乐库 [特殊字符]
  • HDMI外接显示器颜色发灰、过饱和?从显卡设置到硬件校色的完整解决方案
  • Rapid YAML vs 其他YAML库:为什么选择这个高性能解析器?
  • editable-table vs 其他表格插件:为什么选择这个仅120行代码的解决方案
  • AI长内容创作新方案:基于知识库的Agent如何解决上下文断裂问题
  • Check-build高级技巧:如何通过.jshintrc和.eslintrc定制团队规范
  • 【图像融合】基于小波变换全聚焦图像融合matlab代码
  • 从0到1掌握Unlimited-OCR-8bit:新手必看的图像文本提取指南,附PDF扫描实战