AlgoNote「算法通关手册」题解:LeetCode 0336 回文对(Palindrome Pairs)——字典树加速的字符串配对难题
教程文档知识库【免费下载链接】AlgoNote⛽️「算法通关手册」从零开始的「算法与数据结构」学习教程200 道「算法面试热门题目」1000 道「LeetCode 题目解析」持续更新中项目地址https://gitcode.com/gh_mirrors/le/AlgoNote点击查看免费下载本篇题解围绕 docs/solutions/0300-0399/palindrome-pairs.md 展开讲解 LeetCode 0336「回文对」的题目含义、拆分思想与基于字典树Trie的高效实现。读完本文你将掌握如何把「判断两字符串拼接是否为回文」的问题转化为「前缀回文 逆序查找」的组合问题并能在 Python 中手写带索引信息的字典树完成求解。本文同时结合 AlgoNote 仓库中的字典树基础教程与实现源码进行纵深印证。一、题目概述题目链接0336. 回文对标签字典树、数组、哈希表、字符串难度困难题目大意给定一组互不相同的单词列表words要求找出所有不同的索引对(i, j)使得列表中的两个单词words[i] words[j]拼接后能构成回文串。直观上看这是一个「两两配对」问题words列表长度记为n朴素做法是枚举所有(i, j)组合并逐一验证拼接结果单次回文判断需要遍历整个拼接串整体代价为 $O(n^2 \cdot L)$L为单词平均长度。当n较大时这种暴力枚举无法通过。本篇文章给出的字典树解法正是针对「逆序字符串是否存在于单词列表」这一高频查询进行加速的核心思路。二、解题思路把拼接回文拆成「回文段 逆序段」2.1 核心推导设字符串words[i] words[j]能构成回文串。把words[i]拆分成两部分words[i] words_left[i] words_right[i] words[i] words[j] words_left[i] words_right[i] words[j]要让整体成为回文串只需满足下面两个条件words_right[i]本身是回文串words_left[i]与words[j]互为逆序即words[j]恰好等于words_left[i]反转后的结果。同理考虑words[j] words[i]能构成回文串的情形把words[i]拆成words_left[i] words_right[i]words[j] words[i] words[j] words_left[i] words_right[i]此时需要满足words_left[i]本身是回文串words[j]与words_right[i]互为逆序。从上述两个推导可以提炼出统一结论words[j]可以通过拆分words[i]之后逆序得出。于是问题转化为枚举每个单词words[i]的所有拆分位置若某一段前缀或后缀自身是回文则检查剩余一段的逆序字符串是否存在于单词列表中若存在就把对应的索引对加入答案。2.2 算法步骤将整个words列表中的每个单词连同其下标索引插入字典树Trie这样任意字符串都能以 $O(L)$ 时间查询出它是否在列表中出现以及对应的索引值遍历每个单词words[i]遍历该单词的每个拆分位置j把它拆成两个部分words[i][0:j1]和words[i][j1:]若words[i][0:j1]是回文串则查找words[i][j1:]的逆序串是否在字典树中若存在且索引不是i本身则构成一对答案(words[j], words[i])的索引对若words[i][j1:]是回文串则查找words[i][0:j1]的逆序串是否在字典树中若存在且索引不是i本身则构成一对答案(words[i], words[j])的索引对。这里「判断某段字符串是否是回文」以及「查找逆序字符串对应的索引」分别由isPalindrome函数与字典树的search方法完成。三、前置知识字典树Trie及其索引扩展3.1 字典树是什么字典树Trie又称前缀树是一种高效存储和查找字符串集合的树形结构根节点不存储字符其余每个节点存储一个字符从根节点到某个节点的路径恰好组成一个字符串具有相同前缀的单词共用同一条路径。其基本性质包括根节点不存字符其他每个节点只存一个字符从根到某节点的路径组成该节点对应的字符串每个节点的所有子节点字符都不相同。AlgoNote 仓库在 字典树基础教程 中对 Trie 的结构、插入、查找与复杂度做了完整讲解并给出了可运行的实现代码。仓库中的 字典树实现源码 以Node字符节点与Trie字典树两个类组织class Node: # 字符节点 def __init__(self): # 初始化字符节点 self.children dict() # 初始化子节点 self.isEnd False # isEnd 用于标记单词结束 class Trie: # 字典树 def __init__(self): self.root Node() # 初始化根节点根节点不保存字符 def insert(self, word: str) - None: cur self.root for ch in word: if ch not in cur.children: cur.children[ch] Node() cur cur.children[ch] cur.isEnd True def search(self, word: str) - bool: cur self.root for ch in word: if ch not in cur.children: return False cur cur.children[ch] return cur is not None and cur.isEnd def startsWith(self, prefix: str) - bool: cur self.root for ch in prefix: if ch not in cur.children: return False cur cur.children[ch] return cur is not None3.2 回文对题解中对字典树的扩展上面这份实现中search只回答「单词是否存在」。而在 0336 题中我们不仅需要知道逆序字符串是否出现在列表中还需要拿到它对应的单词下标。因此题解代码对节点做了两处扩展每个节点增加self.index -1用于在单词结束节点记录该单词在原列表中的下标insert方法在遍历完整个单词后除了设置isEnd True还把index一并写入search方法在确认目标字符串是完整单词时直接返回其index否则返回-1。这正是「把数据结构稍加改造以承载业务信息」的典型做法字典树不仅能做前缀检索还可以在每个终止节点上挂载附加数据计数、索引、映射值等。仓库中 0208. 实现 Trie (前缀树) 给出了字典树类三件套insert/search/startsWith的标准实现可以作为理解本题 Trie 变体的参照。四、完整题解代码与逐段解读以下是 docs/solutions/0300-0399/palindrome-pairs.md 给出的完整实现class Trie: def __init__(self): Initialize your data structure here. self.children dict() self.isEnd False self.index -1 def insert(self, word: str, index: int) - None: Inserts a word into the trie. cur self for ch in word: if ch not in cur.children: cur.children[ch] Trie() cur cur.children[ch] cur.isEnd True cur.index index def search(self, word: str) - int: Returns if the word is in the trie. cur self for ch in word: if ch not in cur.children: return -1 cur cur.children[ch] if cur is not None and cur.isEnd: return cur.index return -1 class Solution: def isPalindrome(self, word: str) - bool: left, right 0, len(word) - 1 while left right: if word[left] ! word[right]: return False left 1 right - 1 return True def palindromePairs(self, words: List[str]) - List[List[int]]: trie_tree Trie() size len(words) for i in range(size): word words[i] trie_tree.insert(word, i) res [] for i in range(size): word words[i] for j in range(len(word)): if self.isPalindrome(word[:j1]): temp word[j1:][::-1] index trie_tree.search(temp) if index ! i and index ! -1: res.append([index, i]) if temp : res.append([i, index]) if self.isPalindrome(word[j1:]): temp word[:j1][::-1] index trie_tree.search(temp) if index ! i and index ! -1: res.append([i, index]) return res4.1 回文判断函数def isPalindrome(self, word: str) - bool: left, right 0, len(word) - 1 while left right: if word[left] ! word[right]: return False left 1 right - 1 return True双指针从字符串两端向中间收拢一旦发现对应位置字符不等立即返回False全部字符对称则返回True。空字符串长度为 0天然满足回文条件这一点在下面的空串特判中会用到。4.2 构建带索引的字典树trie_tree Trie() size len(words) for i in range(size): word words[i] trie_tree.insert(word, i)把words中每个单词连同其下标插入字典树。由于题目保证单词互不相同每个终止节点上的index是唯一的不会出现覆盖歧义。4.3 枚举拆分位置并配对主循环分两个方向处理对应 2.1 节的两条推导方向一前缀是回文检查后缀的逆序if self.isPalindrome(word[:j1]): temp word[j1:][::-1] index trie_tree.search(temp) if index ! i and index ! -1: res.append([index, i]) if temp : res.append([i, index])若前缀word[:j1]是回文则后缀word[j1:]的逆序串若能匹配到列表中的某个单词索引为index说明words[index] words[i]是回文插入[index, i]特别的当temp 即后缀为空串时说明words[i]本身就是回文此时words[i] words[index]与words[index] words[i]都是回文因此需要同时插入[i, index]。这也是处理「一个完整单词与空串」配对的核心分支。方向二后缀是回文检查前缀的逆序if self.isPalindrome(word[j1:]): temp word[:j1][::-1] index trie_tree.search(temp) if index ! i and index ! -1: res.append([i, index])若后缀word[j1:]是回文则前缀word[:j1]的逆序串若能匹配到单词列表中的某个单词说明words[i] words[index]是回文插入[i, index]。两处均通过index ! i排除「自己与自己拼接」的非法情况并通过index ! -1确认逆序串确实存在于字典树中。4.4 空串与自身回文的边界情况当拆分位置j使得某一段为空时另一段就是完整单词本身。若该单词自身是回文串那么它与空串拼接无论是空串在前还是在后依然构成回文。此时temp 分支会把[i, index]与[index, i]成对补全避免遗漏这种双向答案。五、复杂度分析从代码结构可以推导出以下复杂度结论建树阶段遍历words中全部n个单词并逐个插入每个单词长度为 $L$插入操作与单词长度成正比总时间复杂度为 $O(\sum_{i} L_i)$即所有单词长度之和查询阶段每个单词words[i]被拆分为 $L_i$ 个位置每个位置最多执行两次isPalindrome$O(L_i)$与两次字典树查找$O(L_i)$因此单个单词的代价约为 $O(L_i^2)$整体查询时间复杂度约为 $O(\sum_{i} L_i^2)$空间复杂度字典树节点总数为所有单词的字符总数级别若用哈希表存储子节点本题实现即如此空间复杂度约为 $O(\sum_{i} L_i)$。需要说明的是以上推导基于本仓库题解代码的实现结构LeetCode 官方约束下该解法相对朴素的 $O(n^2 \cdot L)$ 全枚举方案在单词数量大、长度短的测试数据下优势明显而最坏情形单词长度普遍偏长时拆分的平方开销仍不可忽视这是该思路固有的取舍。六、延伸与关联Trie 在字符串配对类题目中的更多应用回文对并不是字典树在本仓库中的唯一用武之地。类似「用一个数据结构加速字符串之间的匹配」的题目还有0425. 单词方块同样是困难题同样是「字典树 搜索」的配合。它利用字典树的startsWith前缀查询能力在回溯过程中根据已选单词逐列推出下一行单词的前缀再在 Trie 中检索所有匹配该前缀的候选词0208. 实现 Trie (前缀树)字典树三件套的模板题insert/search/startsWith是理解本题目Trie变体的基础0005. 最长回文子串回文判断本身是本题的前置技能该题解讲解了回文串的经典判定与最长回文子串的求解思路。如需按主题检索更多题目可查看仓库的 字典树题目列表位于「字典树题目」小节以及 题解总目录其中 0336 回文对被收录在字典树类困难题序列中。七、小结LeetCode 0336「回文对」的难点在于两两配对的数量级太大无法朴素枚举。本题解给出的字典树方案抓住了问题的本质——拼接回文的充要条件可以拆解为「一段自身回文 另一段的逆序存在于单词表」从而把「成对匹配」转化为「逆序字符串的成员查询」。而字典树恰好能以线性于字符串长度的代价完成这种查询配合在每个终止节点上存储单词下标一次search即可同时拿到「是否存在」与「是哪个下标」两个关键信息。最终把完整的 Python 解法沉淀在 docs/solutions/0300-0399/palindrome-pairs.md配合 字典树基础教程 与 字典树实现源码 一起阅读可以完整打通「数据结构原理 → 源码实现 → 实战变形」的学习链路。赞分享教程文档知识库【免费下载链接】AlgoNote⛽️「算法通关手册」从零开始的「算法与数据结构」学习教程200 道「算法面试热门题目」1000 道「LeetCode 题目解析」持续更新中项目地址https://gitcode.com/gh_mirrors/le/AlgoNote点击查看免费下载相关推荐AlgoNote 算法通关手册LeetCode 0009 回文数——不转字符串的整数反转判定法AlgoNote 算法通关手册LeetCode 0009 回文数——不转字符串的整数反转判定法 导读 本篇是「算法通关手册」AlgoNote中 LeetC教程文档知识库AlgoNote 算法通关手册0125 验证回文串——对撞指针与字符串过滤实战详解AlgoNote 算法通关手册0125 验证回文串——对撞指针与字符串过滤实战详解 导读 本文讲解 LeetCode 第 0125 题「验证回文串」的完整解法教程文档知识库AlgoNote「算法通关手册」题解精讲LeetCode 0091 解码方法字符串 动态规划AlgoNote「算法通关手册」题解精讲LeetCode 0091 解码方法字符串 动态规划 导读 本篇是 AlgoNote算法通关手册中 009教程文档知识库上一篇《Go语言高级编程》开源图书全览CGO、Go汇编、RPC 与分布式系统的进阶路线图下一篇CANN ops-math KLDivV2 算子完全指南aclnnKlDiv 接口、计算公式与源码级实现剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

嵌入式设备|多种供电输入方式说明

嵌入式设备|多种供电输入方式说明

一、概述嵌入式智能设备支持多种供电输入方式,适配调试、固定工况、户外便携等不同使用场景,主流包含四种通用供电输入形式,各有适配场景和供电特性。二、四种供电输入方式介绍1. USB供电:标准5V电压输入,供电功率较低…

2026/10/12 1:50:48 阅读更多 →
claude-code-video-toolkit视频节奏同步秘诀:sync_timing与音频锚定时间线消除TTS漂移

claude-code-video-toolkit视频节奏同步秘诀:sync_timing与音频锚定时间线消除TTS漂移

claude-code-video-toolkit视频节奏同步秘诀:sync_timing与音频锚定时间线消除TTS漂移 【免费下载链接】claude-code-video-toolkit AI-native video production toolkit for Claude Code 项目地址: https://gitcode.com/gh_mirrors/cl/claude-code-video-toolkit…

2026/10/12 23:57:06 阅读更多 →
Agent-Reach揭秘:如何构建智能体触达能力,让AI真正能办事

Agent-Reach揭秘:如何构建智能体触达能力,让AI真正能办事

1. 从"能聊天"到"能办事",Agent-Reach 到底解决什么做 AI 应用开发的人应该都有同感:模型本身的能力已经很强,但真正让智能体从"对话玩具"变成"生产力工具"的,恰恰是它能够触达多少外部资…

2026/10/12 23:57:00 阅读更多 →

最新新闻

微信小程序原生课程表开发:周视图与本地存储实战

微信小程序原生课程表开发:周视图与本地存储实战

简介:这份资源是面向微信小程序初学者与前端练手者的原生课程表项目源码,基于微信小程序原生框架实现,可帮助读者快速理解并搭建一个查看、管理每周课程安排的小工具,适合学生、教师及想熟悉小程序开发流程的开发者参考。压缩包为…

2026/10/14 0:32:51 阅读更多 →
C++ std::string 从入门到精通:C++20新特性与性能优化实战

C++ std::string 从入门到精通:C++20新特性与性能优化实战

1. 从C风格字符数组到string:为什么我们不再用char*刚入行那会儿,我接手过一个日志解析模块,满屏都是char buf[1024]和strcpy、strcat。有一次线上崩溃,查了两天才发现是某条日志超长导致缓冲区溢出,把相邻内存踩了。那…

2026/10/14 0:32:51 阅读更多 →
Python turtle绘图从入门到进阶:十几行代码玩转几何分形与随机图形

Python turtle绘图从入门到进阶:十几行代码玩转几何分形与随机图形

别人问我Python入门学什么图形库好,我基本都会先劝一句:别急着上那些花哨的可视化框架,先把turtle玩明白。这个库是Python自带的标准库,装好Python就能用,不需要额外依赖,打开IDLE就能敲出第一张图。虽然它…

2026/10/14 0:32:51 阅读更多 →
AI论文写作工具:查重与润色的高效解决方案

AI论文写作工具:查重与润色的高效解决方案

1. 论文写作的痛点与AI工具的价值写论文最头疼的莫过于查重和润色了。记得我研究生时期,为了把重复率从30%降到10%以下,整整熬了三个通宵手动改写。现在AI工具已经能帮我们解决这些问题,而且效果远超人工操作。目前主流的AI论文工具主要解决三…

2026/10/14 0:32:51 阅读更多 →
Higgsfield AI:短视频爆款的确定性生成引擎

Higgsfield AI:短视频爆款的确定性生成引擎

1. 这不是“AI一键生成”,而是用Higgsfield AI做爆款视频的实战路径最近在几个内容创作群和剪辑师私聊里,反复被问到一个问题:“那个叫Higgsfield AI的工具,真能做出播放量破百万的短视频?”说实话,第一次看…

2026/10/14 0:32:51 阅读更多 →
研发管理系统选型指南:敏捷协作与成本效益分析

研发管理系统选型指南:敏捷协作与成本效益分析

1. 研发管理系统选型困境与测评价值早上开周会时,技术总监又把茶杯重重砸在会议桌上——这已经是本月第三次因为任务进度不透明引发的争执。市场部抱怨需求响应慢,开发团队吐槽需求变更频繁,QA团队则苦于没有统一的测试用例管理平台。这种场景…

2026/10/14 0:31:51 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →