LC 1074. Number of Submatrices That Sum to Target
https://leetcode.com/problems/number-of-submatrices-that-sum-to-target/
这题给我最大的启发就是子矩阵的遍历模式:
- 假设子矩阵由左上角(r0, c0),右下角(r1, c1)定义.
- 首先必须固定子矩阵的左上角r0的值。所以你需要遍历整个r0, 这就是O(n).
- 固定r0之后,接着就需要枚举r1. 当然也可以不枚举,比如要求某个固定行大小的子矩阵。如果要枚举r1的话,这就是O(n).
- 遍历列。这里面可能会使用动态规划的算法等,通常时间复杂度就是O(m).
所以一般来说,想要遍历子矩阵求解某些特性的话,时间复杂度都在O(n^2*m)上面,某些问题可以缩减到O(n*m),但是肯定没有办法再缩减了。
具体到这道题目的话,遵循O(n^2*m)的时间复杂度。在处理每一列的时候,使用hashmap去查找匹配元素的个数。
class Solution: def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: n, m = len(matrix), len(matrix[0]) from collections import Counter if n > m: matrix = list(zip(*matrix)) n, m = m, n # O(n^2 * m) self.ans = 0 for i in range(n): # print('===== {} ===='.format(i)) dp = [0] * m for j in range(i, n): tmp = 0 c = Counter() c[tmp] = 1 for k in range(m): tmp += matrix[j][k] dp[k] += tmp lookup = dp[k] - target self.ans += c[lookup] c[dp[k]] += 1 # print(dp) return self.ans
如果使用上面算法O(n^2m)去套的话会出现超时。这题目解决办法是,使用二分来估算size。
一旦固定好size之后,回到上面的模式,我们可以不用遍历r1了,那么检查算法就是O(nm). 而size的上线是O(min(n,m)), 所以时间复杂度可以是 O(lgn * nm).
其实这题目使用O(n^2m)在n<=300,m<=300上应该也是可以的,但是作者应该还是想规定使用二分吧。
class Solution: def maxSideLength(self, mat: List[List[int]], threshold: int) -> int: n = len(mat) m = len(mat[0]) if n > m: n, m = m, n mat = list(zip(*mat)) dp = [[0] * m for _ in range(n + 1)] for i in range(n): for j in range(m): dp[i + 1][j] = dp[i][j] + mat[i][j] def check_size(sz): for i in range(n - sz + 1): j = i + sz - 1 # use dp[j+1] - dp[i] tmp = [0] * m for k in range(m): tmp[k] = dp[j + 1][k] - dp[i][k] for k in range(1, m): tmp[k] += tmp[k - 1] for k in range(sz - 1, m): # use [k-sz+1 .. k] v = tmp[k - sz] if k - sz >= 0 else 0 if (tmp[k] - v) <= threshold: return True return False s, e = 1, min(n, m) ans = 0 while s <= e: mid = (s + e) // 2 if check_size(mid): ans = mid s = mid + 1 else: e = mid - 1 return ans