1. 题目来源
前置题:
- [M模拟] lc3446. 按对角线进行矩阵排序(对角线遍历+公式推导+模板题)
- 矩形的对角线遍历的基础题。
题单:
- 待补充
2. 题目解析
2025年03月25日21:21:28
方法一:暴力
- 由于这个题目数据量太小了哈,就完全可以暴力来做,每次去暴力判断下左上侧、右下侧对角线的重复元素个数即可。
方法二:前后缀分解
- 暴力显然在同一个对角线上有很多元素被重复遍历。
- 这个时候就需要对角线的前后缀分解。确保重复遍历次数较少。
- 直接遍历每一条对角线元素。
- 从左上方到右下方这样的遍历方向。
- 同时统计左上方对角线的不同元素个数。作为前缀统计,直接放到答案中。
- 再从右下方到左上方这样的遍历方向。
- 此时就可以直接计算答案了,同时统计 右下方 到 左上方 对角线的不同元素个数。作为后缀统计,直接与前缀元素计算答案即可。
- 所以,每个元素至多被遍历两遍。
方法三:状态压缩+前后缀分解
- 这个没啥难度哈,一个简单的状态压缩,将 set 使用一个 uint64 的数字进行代替,是完全可以的哈。
- 注意里面一些位运算的 API 和技巧 即可。
- 时间复杂度: O ( n m ) O(nm) O(nm)
- 空间复杂度: O ( 1 ) O(1) O(1) 返回值不计算
方法二:前后缀分解
func differenceOfDistinctValues(grid [][]int) [][]int {
n, m := len(grid), len(grid[0])
res := make([][]int, n)
for i := range n {
res[i] = make([]int, m)
}
set := map[int]struct{}{}
for k := 1; k < n + m; k ++ {
minJ, maxJ := max(0, m - k), min(m - 1, n - 1 - k + m)
clear(set)
for j := minJ; j <= maxJ; j ++ { // 处理前缀
i := j + k - m
res[i][j] = len(set) // 不包含(i, j) 注意顺序
set[grid[i][j]] = struct{}{}
}
clear(set)
for j := maxJ; j >= minJ; j -- { // 处理后缀,计算答案
i := j + k - m
res[i][j] = abs(res[i][j] - len(set))
set[grid[i][j]] = struct{}{}
}
}
return res
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
方法一:暴力
func differenceOfDistinctValues(grid [][]int) [][]int {
n, m := len(grid), len(grid[0])
res := make([][]int, n)
for i := range n {
res[i] = make([]int, m)
}
getL := func(x, y int) int {
s := map[int]struct{}{}
x --
y --
for x >= 0 && y >= 0 {
s[grid[x][y]] = struct{}{}
x --
y --
}
return len(s)
}
getR := func(x, y int) int {
s := map[int]struct{}{}
x ++
y ++
for x < n && y < m {
s[grid[x][y]] = struct{}{}
x ++
y ++
}
return len(s)
}
abs := func(x int) int {
if x > 0 {
return x
}
return -x
}
for i := range n {
for j := range m {
res[i][j] = abs(getL(i, j) - getR(i, j))
}
}
return res
}