LeetCode2906.构造乘积矩阵【中等】
LeetCode 2906. 构造乘积矩阵
题目链接: https://leetcode.cn/problems/construct-product-matrix/
日期: 2026-03-23
难度: Medium
标签: 前缀和、模运算、取模技巧
一、题目描述
给定一个 n × m 的二维矩阵 grid,定义 p[i][j] 为:矩阵中除 grid[i][j] 外所有元素的乘积,对 12345 取模。
要求返回乘积矩阵 p。
提示:
2 ≤ n × m ≤ 10^51 ≤ grid[i][j] ≤ 10^9- 不能使用除法
二、踩坑回顾
第一次尝试:除法不可行
看到这道题的第一反应是:先算出所有元素的乘积,然后逐个除以当前元素。
long long total = 1; |
问题一:total 会溢出。10^9 的 10^5 次方,这个数字连 long long 都装不下。
问题二:就算我先取模再除,也不行。假设 total = 12345 * 2,取模后 total % 12345 = 0,此时 0 / 2 = 0,而正确答案应该是 2。
关键洞察:除法和取模不能共存
这是本题的核心矛盾。让我把思路理清楚:
total = 12345 * 2 |
原因:取模是一种”单向压缩”。压缩后的数字已经丢失了”哪些因子构成了它”的信息,除法无法逆推回去。
而且本题的 MOD = 12345 = 3 × 5 × 823,不是质数。即使想用乘法逆元来绕过除法,当 grid[i][j] 包含因子 3 或 5 时,逆元根本不存在。
提示的启发:前后缀分解
提示说要用前后缀乘积。乍一看有点懵——前后缀不是一维数组的做法吗?二维矩阵怎么办?
后来想通了:把二维矩阵按行摊平成一维数组,前后缀的思路就自然成立了。
核心思想:
pre[i]= 下标i左边所有元素的乘积suf[i]= 下标i右边所有元素的乘积- 答案
p[i] = pre[i] × suf[i]
这样就完全绕开了除法。
三、解题思路
前后缀乘积的正确打开方式
设摊平后的一维数组为 nums,长度为 L = n × m。
位置 i: [a, b, c, d, ...] |
pre[i]:下标0到i-1的乘积(不含i)suf[i]:下标i+1到L-1的乘积(不含i)
所以 p[i] = pre[i] × suf[i] % MOD。
构造方法
// pre[i] 表示 nums[0...i-1] 的积 |
更进一步的优化:原地两遍扫描
上面用到了三个辅助数组(nums、pre、suf),其实可以更省。
直接利用返回矩阵 p 作为”前缀”的存储介质,两遍扫描搞定:
// 第一遍:计算前缀积,存入 p |
第一遍结束后,p[r][c] 存的是 (r,c) 左上方所有元素的积。
第二遍逆序遍历时,p[r][c] 再乘上 (r,c) 右下方所有元素的积。
由于”当前元素”始终没有被乘进去,完美绕过了”除以自己”的问题。
四、代码实现
class Solution { |
五、复杂度分析
| 维度 | 分析 |
|---|---|
| 时间 | O(n × m) - 两遍扫描 |
| 空间 | O(1) - 除了返回矩阵外,只用几个 long long 变量 |
六、心得总结
为什么这道题值得写成博客?
这道题表面是”矩阵操作”,本质上是模运算的取舍问题。它让我彻底理解了一件事:
取模是一种单向压缩。加法/乘法可以放心取模,除法/逆元不能。
什么时候必须用前后缀分解?
| 场景 | 单遍扫描可行? | 原因 |
|---|---|---|
| 求所有元素和 | ✅ | 减法是加法的逆元 |
| 求所有元素积 | ❌ | 除以自己 ≠ 减去自己 |
| “除自身外的全局统计量” | ❌ | 必须绕过除法 |
前后缀分解的核心思想
把”缺失的元素”想成一个空洞。与其费力去”减去”或”除以”,不如分别在空洞的左边和右边构造积,最后拼起来。
这个思想可以推广到:
- 接雨水(左右最高墙)
- 斐波那契前缀和
- 二维前缀和/积
[!NOTE]
当你发现”减去自己”或”除以自己”做不到时,就该想到”左 + 右”的前后缀分解。
七、相关题目
- LeetCode 238. 除自身以外数组的乘积 - 本题的一维版本
- LeetCode 42. 接雨水 - 前后缀最大值的应用
- LeetCode 560. 和为 K 的子数组 - 前缀和的变体
