LeetCode 2352. 相等行列对
题目描述


题意分析
对方阵的每个行下标
i和列下标j,把第i行从左到右的值,与第j列从上到下的值逐项比较。所有对应位置都相同,才构成一对,求这样的下标对总数。序列顺序必须相同,不是元素集合或总和相同。内容相同但下标不同的行、列仍各自参与计数,因此不能简单去重后统计相等序列的种类数。
解法:行序列编码计数,再逐列查询
核心思路
[!blue]
直接比较每一行与每一列,需要对
n²对序列分别检查n项。可以先将每条行序列编码为一个可作为哈希键的字符串,统计有多少行具有相同内容,再对每一列查询一次。编码按原顺序追加每个整数的十进制表示,并在每项后加分隔符。分隔符明确划开整数边界,避免不同长度数值连在一起产生相同文本。行与列使用同一个编码函数,得到相同键恰好对应逐项相等。
cnt[key]是这种序列的行数,而不是是否出现。每一列查到多少条相同行,就贡献多少个行列下标对;多列具有相同内容时,每列也各查一次,自然得到行重数与列重数的全部组合,不会漏计。读取列时固定列号、让行号从小到大变化。可重复使用同一个整数缓冲数组,因为编码已经生成独立字符串,下一列覆盖缓冲内容不会改变保存的行键或本次查询。
解题步骤
- 逐行使用统一编码函数生成字符串键,在哈希表中累加行频次。
- 对每个列号,按行号递增顺序将整列读入缓冲数组。
- 对缓冲使用同样的编码,查询相同内容的行数并加入答案。
- 处理全部列后返回总下标对数。
代码实现
class Solution {
public int equalPairs(int[][] grid) {
int n = grid.length;
// 键是行的签名,值是重数:内容相同的行可能有多条,必须计数而非去重。
Map<String, Integer> cnt = new HashMap<>();
for (int[] row : grid) {
cnt.merge(key(row), 1, Integer::sum);
}
int ans = 0;
// 复用同一个缓冲区收集列,签名生成后缓冲区可以随意覆写。
int[] col = new int[n];
for (int j = 0; j < n; j++) {
for (int i = 0; i < n; i++) {
col[i] = grid[i][j];
}
// 查到几条同内容的行,这一列就贡献几对。
ans += cnt.getOrDefault(key(col), 0);
}
return ans;
}
private String key(int[] a) {
StringBuilder sb = new StringBuilder();
for (int v : a) {
// 分隔符不可省:否则 [11,1] 与 [1,11] 会撞成同一个签名。
sb.append(v).append(',');
}
return sb.toString();
}
}
import (
"strconv"
"strings"
)
func equalPairs(grid [][]int) int {
n := len(grid)
// 键是行的签名,值是重数:内容相同的行可能有多条,必须计数而非去重。
cnt := map[string]int{}
for _, row := range grid {
cnt[key(row)]++
}
ans := 0
// 复用同一个缓冲区收集列,签名生成后缓冲区可以随意覆写。
col := make([]int, n)
for j := 0; j < n; j++ {
for i := 0; i < n; i++ {
col[i] = grid[i][j]
}
// 查到几条同内容的行,这一列就贡献几对。
ans += cnt[key(col)]
}
return ans
}
func key(a []int) string {
var sb strings.Builder
for _, v := range a {
// 分隔符不可省:否则 [11,1] 与 [1,11] 会撞成同一个签名。
sb.WriteString(strconv.Itoa(v))
sb.WriteByte(',')
}
return sb.String()
}
复杂度分析
- 时间复杂度:固定整数位宽下期望 $O(n^2)$。行列各编码
n条长度为n的序列,字符串构造与哈希总字符量同为平方级。- 空间复杂度:$O(n^2)$,最多保存
n个长度为 $O(n)$ 的行签名,列缓冲另占 $O(n)$。
关键点总结
[!green]
- 序列编码保留元素顺序,并用分隔符保留数值边界。
- 哈希表保存重数,一列匹配多行时要累加全部行数。
- 每个列下标独立查询,重复列也各自产生对应下标对。
- 列缓冲可以复用,已经生成的字符串键不依赖缓冲后续内容。
易错点总结
[!yellow]
- 编码时省略分隔符,不同整数边界可能拼成完全相同的字符串。
- 用集合替代计数表,内容相同的多条行只保留一次,丢失不同下标对。
- 将行列内部排序后再比较,接受了仅元素相同但原顺序不同的序列。
- 从下到上读取列,却从左到右读取行,改变了题目规定的比较方向。
- 只比较行和列的元素总和,和相同远不足以保证每项相同。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 867. 转置矩阵 | 简单 | 同样按列读取矩阵,转置后列变成行可帮助理解本题的序列比较。 |
| 49. 字母异位词分组 | 中等 | 同样把对象映射到规范签名再分组,本题必须保留原序列顺序,不能像异位词那样排序元素。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!