滑动窗口解最小覆盖子串:双指针与哈希表实现 O(n) 匹配
“滑动窗口”这四个字在算法面试里的出场率高到离谱而“最小覆盖子串”又是滑动窗口家族里最能检验功力的那一道。字符串覆盖怎么判断、窗口什么时候该收缩、计数器状态怎么同步哪一环掉链子代码表面上跑得通换一组测试用例就翻车。这篇文章我会把这题从头到脚拆一遍题意陷阱、双指针原理、完整可运行的代码、我调试中踩过的坑以及几道同框架的变式题。无论你是面试冲刺还是想把滑动窗口真正吃透这篇都值得认真过一遍。1. 先看懂题目最小覆盖子串到底在问什么1.1 题目本质拆解题目一句话描述给定字符串 s 和 t在 s 中找出包含 t 的所有字符的最短连续子串。包含的意思是“种类和数量都足够”顺序无所谓。比如 s ADOBECODEBANCt ABC答案是 BANC不是 DOBEC 也不是 BECODEBA因为要同时覆盖一个 A、一个 B、一个 C且子串连续。这里最容易被忽略的细节有三个。第一t 里的字符可能重复。t AABC那窗口里至少要有两个 A、一个 B、一个 C只有一个 A 就不算覆盖。这是“覆盖”和“包含字符集合”的本质区别很多初学者在这里栽跟头。第二结果必须是 s 的连续子串不能跳着取。这一点把题目牢牢锁死在“滑动窗口”的范畴里而不是“子序列”问题。子序列可以用贪心或动态规划子串必须靠窗口或者前缀数组一类的手段。第三如果 s 中根本找不到这样的子串返回空串。这个边界条件看着简单但实际写代码时很容易在处理初始化值时漏掉最后返回一个错误的结果。1.2 为什么暴力解法会卡死最直觉的做法是枚举所有子串。两层循环固定左右端点O(n^2) 个子串每个子串再扫描一遍确认是否覆盖 t总复杂度 O(n^2 * m)n 是 s 长度m 是 t 长度。字符串一长就直接爆炸别说是面试官你自己跑一遍用例都会怀疑人生。这个复杂度高在哪高在“重复计算”。窗口从 [0, 5] 扩到 [0, 6]多了一个字符但暴力解法不会利用上一次的判断结果而是把整个 [0, 6] 重新扫描一遍。实际上窗口范围存在大量重叠这些重叠部分的信息被白白浪费了。滑动窗口解决的就是这个问题通过维护左右两个指针让每个字符最多被进入窗口一次、移出窗口一次判断逻辑从 O(n^2 * m) 降成 O(n)这才是这道题真正的考点。1.3 最容易理解错的几种样例我在给别人讲这题时发现有些样例特别能暴露理解偏差。s aat aa答案应该是 aa 而不是 a因为 t 需要两个 a。s abt aabs 里总共只有一个 a无论取哪个连续子串都不可能凑出两个 a返回空串。s bbat ab答案是 ba。窗口必须从左往右覆盖到 b 和 a虽然 b 出现的顺序和 t 里的 a、b 相反但覆盖不要求顺序所以 ba 合法。把这三个用例在纸上跑一遍窗口扩缩的直觉基本上就建立起来了。2. 滑动窗口的核心思路为什么它能做到 O(n)2.1 从“窗口”的直觉说起想象你站在一排货架前要从里面挑出满足一份购物清单的最短一段货架区间。如果当前区间已经包含了清单上的所有东西那就把区间左端往右缩看能不能缩短一旦缩到不满足就再把右端往右拉扩大区间。这个“先扩后缩、扩了再缩”的过程就是滑动窗口的物理直觉。放到这题里右指针负责“找可行解”左指针负责“找最优解”。右指针不断向右扩展让窗口逐渐满足覆盖条件一旦满足就尝试移动左指针收缩窗口直到再次不满足。每次收缩时记录当前窗口的长度这个长度就是“以当前右指针为终点”的最短覆盖子串。右指针继续前进重复这个过程所有候选结果都会遍历到。为什么这样不会漏掉答案因为任何最短覆盖子串 [L, R] 都一定是在右指针到达 R 时、左指针收缩到 L 的那个瞬间被记录的。如果右指针还没到 R窗口覆盖不了 t如果右指针超过了 R那该子串已经被提前记录过更短的版本。所以一次线性遍历所有可能的最优窗口都会被覆盖到。2.2 如何判断窗口是否覆盖了 t覆盖判断是这道题的核心工程。最笨的办法是每次比对两个哈希表但这样一次判断就要 O(m)窗口移动 n 次复杂度又回去了。正确做法是引入一个状态变量把覆盖判断变成 O(1)。我常用的方案是三个角色need、window、matchedCnt。need 记录 t 中每个字符的需求量。window 记录当前窗口里每个字符的出现次数。matchedCnt 记录“当前窗口内已经满足了需求数量的字符种类数”。当一个字符进入窗口后如果它的 window 计数恰好等于 need 需求说明这个字符从“欠”变成了“结清”matchedCnt 加一。同理一个字符从窗口移出时如果移出前它恰好处于结清状态移出后就欠账了matchedCnt 减一。这里有一个关键细节只有“恰好相等”时才改变 matchedCnt而不是“大于等于”。因为 window 可能超过 need超出的部分是多余的不影响覆盖状态。如果每次都判断 window[c] need[c] 就可能重复计数一个字符被多次标记为满足。这也是新手最容易写错的地方。当 matchedCnt 等于 need 的不同字符种类数时窗口就是覆盖状态可以进入收缩阶段。整个判断过程不扫描 t不遍历窗口两个计数操作都是 O(1)。2.3 状态更新的顺序为什么这么敏感我见过太多版本代码逻辑看起来一样唯一区别是把 window[d]-- 放在一行还是两行结果一个对、一个错。问题就出在相等判断的时机。收缩左侧时正确的顺序是先检查当前窗口里这个字符的数量是否恰好等于需求若是matchedCnt 先减一然后把窗口计数减一。反过来先 window[d]-- 再去判断 window[d] need[d]条件几乎永远不成立因为计数已经变了。扩张右侧时同理先把字符装进窗口再判断装完后是否恰好等于需求。如果先判断再装永远到不了“等于”的状态。这个顺序问题本质上就是“状态变更前后哪一刻才是满足条件的那一瞬间”。收缩时满足状态结束于计数减一之前扩张时满足状态开始于计数加一之后。把这两个瞬间搞清楚代码就不会出错。3. 完整实现代码与每一步的意图3.1 C 参考实现逐段拆解string minWindow(string s, string t) { if (s.empty() || t.empty()) return ; // 统计 t 的字符需求 unordered_mapchar, int need; for (char ch : t) need[ch]; // needCnt 是 t 中不同字符的种类数 int needCnt need.size(); unordered_mapchar, int window; int matchedCnt 0; int left 0, right 0; int minLen INT_MAX, start 0; while (right s.size()) { char c s[right]; right; // 只有 t 中出现的字符才需要计数 if (need.count(c)) { window[c]; if (window[c] need[c]) { matchedCnt; // 该字符从欠账变成结清 } } // 窗口已经覆盖 t尝试收缩左边界 while (matchedCnt needCnt) { // 当前窗口长度right 已经自增所以直接用 right - left if (right - left minLen) { minLen right - left; start left; } char d s[left]; left; if (need.count(d)) { // 注意先判断再减少计数 if (window[d] need[d]) { matchedCnt--; // 该字符从结清变回欠账 } window[d]--; } } } return minLen INT_MAX ? : s.substr(start, minLen); }逐段看。初始化部分先把 t 的需求统计出来need 的大小就是“有多少种字符需要满足”。主循环里右指针每次读入一个字符并自增所以窗口长度可以直接用 right - left不需要再加一。收缩部分当 matchedCnt needCnt 时说明当前窗口合法。此时先记录答案然后移动左指针。移动前要判断移出的字符是否影响覆盖状态如果它移出前恰好数量达标移出后就欠账matchedCnt 减一否则不影响。最后如果 minLen 没有更新过说明 s 里根本找不到覆盖子串返回空串。3.2 Python 版本与“一行之差”def minWindow(s: str, t: str) - str: if not s or not t: return need {} for ch in t: need[ch] need.get(ch, 0) 1 needCnt len(need) window {} matchedCnt 0 left 0 minLen float(inf) start 0 for right in range(len(s)): c s[right] if c in need: window[c] window.get(c, 0) 1 if window[c] need[c]: matchedCnt 1 while matchedCnt needCnt: # 这里的窗口长度是 right - left 1 if right - left 1 minLen: minLen right - left 1 start left d s[left] left 1 if d in need: if window[d] need[d]: matchedCnt - 1 window[d] - 1 return if minLen float(inf) else s[start:start minLen]注意 Python 版里 for 循环的 right 没有自增所以窗口长度要写成 right - left 1。这和 C 版是同一个逻辑、两种表达抄代码时最容易在这种地方翻车。我建议选定一种语言后就把长度计算方式固定下来凡是 right 指向“已处理区间的右端”就加一凡是 right 指向“下一个待处理位置”就不加。3.3 复杂度分析与常数优化时间复杂度 O(n m)。m 是统计 t 的开销n 是主循环的开销。主循环里每个字符至多进入窗口一次、移出窗口一次窗口内操作是常数级所以总操作次数是线性。空间复杂度 O(字符集大小)用哈希表时实际上只需要存 t 中出现的字符种类数用数组存全部字符集会固定为 128 或 256 的常数空间。字符集固定的情况下可以用数组替代哈希表追求更快的常数。例如题目表明只包含英文字母时开 int need[128] 和 int window[128]每个字符直接用 ASCII 码索引避免哈希计算实测在大字符串上能快不少。不过如果是 Unicode 或者字符范围不确定的题目哈希表更稳不要盲目为了性能牺牲正确性。还有一种更紧凑的写法把 need 数组初始化成负的需求量窗口每遇到一个字符就 need[c]当这个值等于 0 时说明从欠账变成结清。这种写法省掉 window 数组但理解成本更高我建议先掌握双哈希版本的思路再去玩这种优化。4. 踩坑实录我调试最久的几个 Bug4.1 常见错误与根因速查表错误表现根本原因修复思路答案比预期长包含了多余字符matchedCnt 更新时机错误窗口已经满足但不进入收缩把“恰好相等”作为计数变化的唯一条件返回空串但实际存在覆盖子串minLen 初始化过大或 start 更新条件写错初始化 minLen 为 INT_MAX且只在更短时更新某些字符被重复计数判断了 window[c] need[c] 而不是 改成严格相等超出部分不影响覆盖状态收缩一步后窗口仍合法但代码提前停止收缩while 条件误写成 if收缩必须用 while一直缩到不满足为止固定字符集用例通过随机字符出错哈希表 vs 数组索引混乱明确字符范围统一用其中一种方案第一个坑最隐蔽。我最早写这题时matchedCnt 在字符出现时就递增而不是在“恰好满足需求”时递增。窗口中有两个 A需求也是一个 A结果 A 被算成满足两次窗口明明合法却没能及时收缩答案长度偏大。此后我把“结清”和“超过”彻底分开遇到所有大于需求的情况全部不改变 matchedCnt问题才解决。4.2 必写的边界测试集合每次写完代码我建议至少跑一组如下测试覆盖大多数边界情况。输入期望输出考察点s ADOBECODEBANC, t ABCBANC标准场景顺便验证窗口能正确从 ADOBEC 收缩到 BANCs a, t aa单个字符直接覆盖s a, t aa数量不足返回空串s aa, t aaaa重复字符需求且窗口不能收缩成单字符s ab, t bb窗口需要先扩到覆盖再缩到最短s bba, t abba不要求顺序验证覆盖定义s , t a空串输入s a, t t 为空按约定返回空串其中 s bba 这个用例特别能验证窗口的收缩逻辑。右指针走到最后一个字符 a 时窗口是 bba合法但太长左指针前移先移出 b窗口变成 ba仍然合法于是记录 ba再移出 b窗口只剩 a不合法停止收缩。最终答案 ba。每一步都该在心里过一遍。4.3 一个开挂的预处理技巧还有一个很少人提的优化在正式滑动之前先快速判断 s 是否具备覆盖 t 的基本条件。做法是统计 s 的字符频率如果某个字符在 s 中出现的次数小于 t 的需求量那 s 里永远找不到合法子串直接返回空串。这个预处理是 O(n m) 的不会改变整体复杂度但它能拦住一类极端用例。比如 s 是一个长度一万的字符串t 里有一个字符 s 完全没有不预处理的话主循环要完整跑完一遍、记录 minLen 为 INT_MAX 才知道答案是空。预处理可以在第一轮就返回省掉不必要的窗口操作。我通常在写模板时把这个判断也加上代码多两行鲁棒性提升一截。5. 一通百通滑动窗口还能干什么5.1 同框架的经典变式题对比最小覆盖子串不止是一道题它是滑动窗口模板的“母题”。把它吃透以后下面几类题目可以无缝迁移。题目类型窗口内维护的信息收缩时机模板改动点无重复字符的最长子串字符出现次数出现重复字符时收缩不需要 need 和 matchedCnt用一个重复标志判断字符串排列t 中字符出现次数与种类数匹配且长度等于 t 的长度将窗口长度固定每次扩缩同步进行找到字符串中所有字母异位词同上固定窗口长度维护匹配状态记录每个合法窗口的起始索引最小覆盖子串同上匹配后尽量收缩最短原始模板动态扩缩无重复字符的最长子串虽然没有“两个字符串”的概念但核心同样是右指针扩展、遇到冲突后左指针收缩收缩目标是消除重复代码结构几乎一样。字符串排列和异位词则是最小覆盖子串的“固定窗口版本”区别在于满足条件后还要检查窗口长度是否等于目标长度等于才记录答案。这里要特别提醒一个容易混淆的题最短超序列或者覆盖子序列。如果题目要求的是“子序列”而不是“子串”滑动窗口的哈希表方案就失效了因为子序列允许跳字符连续性约束没了需要换贪心或动态规划思路。面试时如果不确定题目语义务必先和面试官确认清楚。5.2 滑动窗口在工程中的应用滑动窗口不只是刷题概念工程里到处是它的身影。信号处理里常见的滑动窗口滤波就是维护固定长度的数据窗口每次滑入新数据、滑出旧数据再对窗口内数据做移动平均或加权平均用于传感器去噪、平滑曲线。这和算法题里的“固定窗口长度”变式非常接近。硬件领域也能看到它的变体。比如 Verilog 实现卷积或图像处理时需要维护一个滑动的数据窗口每个时钟周期移入一个像素、移出一列数据用寄存器组缓存窗口内容。思想同样是“复用滑动过程中的重叠数据避免重复搬运”。网络里的滑动窗口协议、分布式系统里的滑动窗口限流本质也都是“维护一个动态区间根据区间内的状态做出决策”。刷题时掌握的这套“右扩、左缩、状态同步”的思维方式放到这些场景里依然成立。这也是为什么面试官执着于考查滑动窗口的原因它不只是算法的套路更是工程中的通用思维模型。5.3 面试怎么讲才能让面试官点头面试遇到这道题建议按这个顺序表达思路。第一明确题意。主动询问 t 是否可能包含重复字符、返回要求是什么、s 为空怎么办。这步走完面试官会觉得你具备需求分析意识。第二给出暴力解并分析复杂度。不要上来就写最优解先说两层循环枚举、每次比较哈希表复杂度 O(n^2 * m)说明你知道为什么需要优化。第三引出滑动窗口。点出重叠子问题的存在窗口扩大一个字符时之前的覆盖信息可以复用所以用双指针滑动。到这里面试官一般会追问“你如何判断窗口是否覆盖”这时再把 need、window、matchedCnt 的设计讲清楚重点强调“恰好相等才更新状态”这一细节。第四写代码时主动讲边界。比如右指针自增后长度计算方式、左指针移出字符时先判断再减计数、minLen 初始化为 INT_MAX 的原因。每写一行关键代码就补一句意图面试官体验会好很多。如果被追问字符集变大怎么办回答用哈希表而非固定数组。被追问能否优化常数回答固定字符集下用数组替代哈希表。被追问如果要求返回所有最短覆盖子串呢思路是在收缩匹配时把长度等于 minLen 的起始位置都记录下来最后统一收集结果。这些扩展点想清楚这道题就算真的拿下了。我自己刷这道题的经验是别急着写代码。先把窗口扩缩的过程在纸上用手画两遍画出一个 4 字符的 s 和一个 2 字符的 t把 right 扩张、left 收缩、matchedCnt 从 0 到 2 再到 1 的变化全部标出来。画明白了代码十有八九一遍过。这个“先画窗口再写代码”的习惯后来我刷无重复字符最长子串、字母异位词时也在用每道都能少调很长时间的 bug。

相关新闻

Redis为什么快?五层设计原理与生产性能优化实践

Redis为什么快?五层设计原理与生产性能优化实践

今天聊聊一个经典到不能再经典的面试题:Redis 为什么这么快?这题几乎每次招人都会问,但答好的人真不多。多数人上来就甩一句“因为它是内存数据库”,然后就没有然后了。这个回答对不对?对,但只说明你背过答…

2026/10/9 6:24:16 阅读更多 →
Agent-Reach:用触达率量化AI Agent能力边界与调优实践

Agent-Reach:用触达率量化AI Agent能力边界与调优实践

1. 先聊清楚:Agent-Reach到底想解决什么问题1.1 这个项目的由来:Agent能力的“黑色一公里”做AI Agent开发的同行应该都有这种感觉:模型本身的能力已经不错了,但把模型包装成一个“能办事的智能体”之后,效果就完全不是…

2026/10/9 6:23:16 阅读更多 →
CNN模型保存与加载实战:PyTorch最优模型checkpoint全解析

CNN模型保存与加载实战:PyTorch最优模型checkpoint全解析

训了整整一晚的CNN卷积神经网络,早上爬起来看训练日志,loss曲线一路向下,漂亮得像教科书里的插图。可把训练完的模型丢到测试集上一跑,准确率比验证集上看到的低了将近5个点。我相信训过卷积神经网络的人,多多少少都撞…

2026/10/9 6:23:16 阅读更多 →

最新新闻

可靠性测试别只会跑温箱振动台:失效物理与加速寿命是关键

可靠性测试别只会跑温箱振动台:失效物理与加速寿命是关键

干我们这行的,提起“可靠性测试”,不少人第一反应是:把样品扔进温箱里烤一烤、冻一冻,再放振动台上摇一摇,出来没坏就算通过。要是真这么想,那可靠性测试就白做了。作为一个和温箱、振动台、耐久跑法打了十…

2026/10/9 7:02:48 阅读更多 →
JVM内存模型与调优实战:从Minecraft OOM到HMCL配置

JVM内存模型与调优实战:从Minecraft OOM到HMCL配置

很多朋友第一次真正意识到 JVM 的存在,不是在 Java 课堂上,而是在一个完全不相关的场景里——玩游戏的时候。我用 HMCL 启动器给 Minecraft 装了个整合包,点了启动,等了两分钟,游戏闪退。把日志拉到最底部,…

2026/10/9 7:02:48 阅读更多 →
VS Code AI 语言模型配置全指南:模型切换、思维强度与 BYOK 自有密钥接入

VS Code AI 语言模型配置全指南:模型切换、思维强度与 BYOK 自有密钥接入

文档教程 【免费下载链接】vscode-docs Public documentation for Visual Studio Code 项目地址: https://gitcode.com/gh_mirrors/vs/vscode-docs 点击查看 免费下载 本文基于 Visual Studio Code 官方文档仓库(vscode-docs)中的 docs/agen…

2026/10/9 7:02:48 阅读更多 →
多标签文本分类实战复盘:从Embedding到Transformer的TAAC优化之路

多标签文本分类实战复盘:从Embedding到Transformer的TAAC优化之路

1. 从"vibe coding"说起:一个新手小白的TAAC复盘到底在复盘什么第一次看到"vibe coding"这个词,我脑子里蹦出来的画面是:一个人对着编辑器,凭感觉敲代码,跑通了就欢呼,跑不通就换一种写…

2026/10/9 7:02:48 阅读更多 →
内容团队如何用Qoder构建标准化AI工作流与协作机制

内容团队如何用Qoder构建标准化AI工作流与协作机制

团队里六个人,过去半年试过不下四个AI工具,从网页版问答到各种套壳应用,最后都回到同一个问题:AI确实能干活,但每个人干出来的活参差不齐,提示词散落在各自收藏夹里,换个项目就抓瞎。真正让我下…

2026/10/9 7:02:48 阅读更多 →
日期处理陷阱:从1月25日看时区与历法边界

日期处理陷阱:从1月25日看时区与历法边界

我很少拿一个日期当文章标题,但1月25日这个数字,我记了快一整年。不是因为它特殊——公历里它既不是节日也不算节气,每年对应的星期几、农历日子完全不一样。正因为它"每天都在变、又好像什么都没变",才在交付前一周把我…

2026/10/9 7:01:47 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练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/9 6:17:20 阅读更多 →