1. 先搞清楚题目在问什么:这题根本不是搜索题
LeetCode 2943“最大化网格图中正方形空洞的面积”,我第一次在周赛里碰见它,顺手就想到了 DFS 染色或者网格 BFS——毕竟题目里又是“网格图”又是“空洞”,很难不往二维搜索方向联想。但如果你真拿一个二维矩阵去标记哪些格子被删,然后辛辛苦苦扫连通块,多半会写着写着发现又蠢又没必要。这题的 tag 虽然叫 medium,实际难度更多地落在“能不能把二维场景抽象成一维连续段”上,代码量反而小得可怜。
先说清楚题要表达的场景:网格本身有若干条横向隔断和纵向隔断,把大区域切成一个一个的小方格。现在系统中给出两组坐标,分别表示哪些横向隔断、哪些纵向隔断处于“可通行/被拆除”状态。我们希望在这些隔断都被拆除之后,能从网格里圈出一个完整的方形空洞,空洞内部不再有任何横竖隔断阻挡,问这个空洞的面积最大能到多少。
这看起来像是个铺地板的问题,但真正要关心的不是每个格子的状态,而是“横向到底能打通多长的一段连续区域,纵向到底能打通多长的一段连续区域”。一旦把注意力从格子转移到隔断线上面,解法立刻就清晰了。
我最后提交的代码不超过三十行,核心就两个步骤:
- 对横向隔断坐标排个序,找出最长的连续坐标段。
- 对纵向隔断坐标排个序,同样找出最长的连续坐标段。
- 取两者中较小的值作为正方形边长,边长平方就是答案。
下面我会把每一步推导、实现以及我在写过程中踩过的几个坑完整展开。如果你正在刷周赛题或者准备面试,建议把这篇的思路和模板记下来,因为这类“删除若干条线后求最大连续跨距”的题,在近几次周赛里出现频率不低。
1.1 空洞的本质:横竖两条“连续贯通带”的交集
先打个比方。想象你面前有一排多米诺骨牌,骨牌与骨牌之间有一些小挡板。如果某些挡板被抽掉,你就获得了从一块骨牌滑到另一块骨牌的空间。问题是:你能获得多长的一段无阻挡滑动区?
网格图里的横向隔断和纵向隔断就是这样的小挡板。假设网格原来被 n 条横向隔断分成 n+1 层区域,被 m 条纵向隔断分成 m+1 列区域。当你拆除一条横向隔断时,它上下两侧的格区就连通了;拆除多条相邻的横向隔断时,整个纵向跨度会变得更大。
注意“相邻”这个词,它是整道题的题眼。
如果拆除的横向隔断坐标是 [1, 2, 4],坐标 1 和 2 相邻,它们能合并出跨过 3 个横向格区的大空隙;而坐标 4 是孤立的一段,它只能单独支撑跨过 2 个格区的小空隙。纵向的分析完全一样。所以一个完整空洞要能框出一个正方形,就要横向存在一个足够大的连续贯通带,纵向也存在一个足够大的连续贯通带,两个带子交叠出来的矩形区域就是空洞。
正方形特有的约束是宽高必须相等。如果横向能贯通 5 个格宽,纵向只能贯通 3 个格高,那最优不可能让空洞的边长比 3 更大,多出来的横向宽度在这个正方形里没有意义。
1.2 为什么要从“隔断条数”换算成“可占格数”
这一步是我最初最容易犯糊涂的地方。假设你在坐标集合里找到了连续 3 条横向隔断都被拆除,那么上下方向能腾出的格区数量并不是 3,而是 4。
可以拿最朴素的例子验证:一个 2 x 2 的网格,原本有四格,中间有一横一竖两条隔断把它分成四份。如果横坐标、纵坐标都只有一条可被拆除,也就是连续段长度为 1,那么拆除后整个 2 x 2 区域都会变成一个没有内部障碍的空洞。空洞边长显然是 2,不是 1。所以真实边长要按“连续拆除条数 + 1”计算,1 条隔断对应两格,2 条连续隔断对应三格,这个关系在任何维度上都成立。
我在初版代码里直接拿连续段长度作为边长去平方,测试样例输出了一个非常离谱的 1,就是因为漏了这条规则。把加一操作补上后,这道题的答案逻辑才真正闭合。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一行心法:怎么高效算出一维最长连续段
既然横向和纵向处理方式完全相同,你就可以把精力集中在写一个干净的一维工具函数上。
输入是一个可能乱序的整数数组,数组元素表示被拆除的隔断坐标。要返回的值是“这个数组里最长连续相邻坐标的长度是多少”。比如 [3, 1, 2],排序后是 [1, 2, 3],最长连续段为 3;[5, 1, 4, 2] 排序后是 [1, 2, 4, 5],最长连续段分别是 2 和 2,所以结果也是 2。
最直白的做法是排序后一次线性扫描,完全足够应付大范围数据。因为排序本身是 O(N log N),而后续扫描只有 O(N),整体瓶颈在前面。后面你如果想优化到严格 O(N),可以用哈希集合做类最长连续序列的算法,但考虑到输入规模,排序方案的代码更短,也更不容易写错。
2.1 一次扫描的完整细节
写这个工具函数时,我建议你用“当前连续长度”和“全局最长连续长度”两个变量来滚。先排序,然后从第二个元素开始遍历,比较当前元素和上一个元素的关系:
- 如果当前元素等于上一个元素加 1,说明连续段还在继续,当前连续长度加 1。
- 如果当前元素等于上一个元素本身,说明输入里有重复坐标,直接跳过,不能算作连续,也不能重置连续计数。
- 其他情况说明断开了,当前连续长度重置为 1。
遍历过程中不断用当前连续长度刷新全局最大值。
这里有一个很多
