Problem: 2975. 移除栅栏得到的正方形田地的最大面积
文章目录
- 整体思路
-
-
- [1. 核心问题](#1. 核心问题)
- [2. 算法逻辑](#2. 算法逻辑)
-
- 完整代码
- 时空复杂度
-
-
- [1. 时间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)](#1. 时间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2))
- [2. 空间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)](#2. 空间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2))
-
整体思路
1. 核心问题
我们需要在一个 m × n m \times n m×n 的矩形田地中,选择水平栅栏(hFences)和垂直栅栏(vFences),构建一个正方形区域。
- 田地的边界分别是水平线
1和m,垂直线1和n。 - 输入给定的
hFences和vFences是中间的栅栏,边界栅栏默认存在。 - 目标:找到可能形成的正方形的最大面积。如果无法形成,返回 -1。
2. 算法逻辑
正方形的边长取决于两个栅栏之间的间距。
- 如果在水平方向上存在两个栅栏,它们之间的距离为 L L L。
- 如果在垂直方向上也存在两个栅栏,它们之间的距离也为 L L L。
- 那么这两个间距组合起来就能围成一个边长为 L L L 的正方形。
步骤:
- 预处理栅栏 :对于水平和垂直方向,都要把边界 (
1和m或n)加入到给定的栅栏数组中。 - 计算所有可能的间距 :
- 分别计算水平方向所有可能的间距集合
hSet。 - 分别计算垂直方向所有可能的间距集合
vSet。 - 这里使用双重循环遍历排序后的栅栏数组,计算任意两根栅栏之间的距离
fences[j] - fences[i]。
- 分别计算水平方向所有可能的间距集合
- 寻找交集最大值 :
- 遍历其中一个集合(如
hSet),检查其中的间距是否也存在于另一个集合vSet中。 - 如果是,说明这个长度 L L L 既可以是宽也可以是高,能构成正方形。更新最大边长
ans。
- 遍历其中一个集合(如
- 计算结果 :如果找到了合法的正方形(
ans > 0),返回面积并取模;否则返回 -1。
完整代码
java
import java.util.*;
class Solution {
final int MOD = 1_000_000_007;
public int maximizeSquareArea(int m, int n, int[] hFences, int[] vFences) {
// 1. 获取水平方向所有可能的间距集合
// m 是水平方向的边界坐标 (行数)
Set<Integer> hSet = gapSet(hFences, m);
// 2. 获取垂直方向所有可能的间距集合
// n 是垂直方向的边界坐标 (列数)
Set<Integer> vSet = gapSet(vFences, n);
// 记录最大合法边长
int ans = 0;
// 3. 寻找两个集合的交集中的最大值
// 遍历水平间距集合
for (int x : hSet) {
// 如果垂直方向也有相同的间距 x
if (vSet.contains(x)) {
// 更新最大边长
ans = Math.max(ans, x);
}
}
// 4. 计算面积并返回
if (ans == 0) {
return -1; // 没有找到任何匹配的间距(实际上通常至少有 min(m-1, n-1),除非输入限制特殊)
}
// 面积可能很大,需要转换成 long 进行乘法,然后取模
return (int)((long) ans * ans % MOD);
}
// 辅助方法:计算所有可能的栅栏间距
// fences: 内部栅栏坐标数组
// boundary: 边界坐标 (对应 m 或 n)
private Set<Integer> gapSet(int[] fences, int boundary) {
int len = fences.length;
// 创建新数组,长度比原数组多 2,用于存放边界 1 和 boundary
// fences 本身是引用传递,为了不修改原数组并加入新元素,使用 copyOf
fences = Arrays.copyOf(fences, len + 2);
// 添加边界栅栏
fences[len] = 1;
fences[len + 1] = boundary;
// 排序,方便从小到大计算间距
Arrays.sort(fences);
// 使用 HashSet 存储所有可能的间距,自动去重
Set<Integer> st = new HashSet<>();
// 双重循环枚举任意两根栅栏
for (int i = 0; i < fences.length; i++) {
for (int j = i + 1; j < fences.length; j++) {
// 计算间距并加入集合
st.add(fences[j] - fences[i]);
}
}
return st;
}
}
时空复杂度
假设 hFences 长度为 H H H,vFences 长度为 V V V。
1. 时间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)
gapSet函数 :- 排序: O ( H log H ) O(H \log H) O(HlogH)。
- 双重循环枚举所有间距:这是一个组合问题,次数为 C ( H , 2 ) ≈ O ( H 2 ) C(H, 2) \approx O(H^2) C(H,2)≈O(H2)。
- HashSet 插入操作平均 O ( 1 ) O(1) O(1)。
- 因此,处理水平栅栏耗时 O ( H 2 ) O(H^2) O(H2),处理垂直栅栏耗时 O ( V 2 ) O(V^2) O(V2)。
- 交集查找 :
- 遍历
hSet,其大小最多为 O ( H 2 ) O(H^2) O(H2)。 - 在
vSet中查找,平均 O ( 1 ) O(1) O(1)。 - 这部分耗时 O ( H 2 ) O(H^2) O(H2)。
- 遍历
- 总计 : O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)。
- 注意:如果 H H H 或 V V V 很大(例如 10 5 10^5 105),这个 O ( N 2 ) O(N^2) O(N2) 算法会超时。但根据题目(LeetCode 2975)的数据约束,栅栏数量通常较小(如 600),允许 O ( N 2 ) O(N^2) O(N2) 通过。
2. 空间复杂度: O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)
- 计算依据 :
- 我们需要两个 HashSet 来存储所有可能的间距。
- 水平方向最多有 ( H + 2 ) ( H + 1 ) 2 \frac{(H+2)(H+1)}{2} 2(H+2)(H+1) 个间距,即 O ( H 2 ) O(H^2) O(H2)。
- 垂直方向最多有 O ( V 2 ) O(V^2) O(V2) 个间距。
- 结论 : O ( H 2 + V 2 ) O(H^2 + V^2) O(H2+V2)。
参考灵神