有了 row 数组以后,如果要统计以 (i,j) 为右下角满足条件的子矩形,我们就可以枚举子矩形的高,即第 k 行,看当前高度有多少满足条件的子矩形。由于我们知道第 k 行到第 i 行「每一行第 j 列向左延伸连续 1 的个数」 row}[k][j],\textit{row}[k+1][j],\cdots,\textit{row}[i][j],因此我们可以知道第 k 行满足条件的子矩形个数就是这些值的最小值,它代表了「第 k 行到第 i 行子矩形的宽的最大值」,公式化来说,即:
\min_{l=k..i} {\textit{row}[l][j]}
因此我们倒序枚举 k,用 col 变量来记录到当前行 row 的最小值,即能在 O(n) 的时间内统计出以 (i,j) 为右下角满足条件的子矩形个数。
classSolution { public: intnumSubmat(vector<vector<int>>& mat){ int n = mat.size(); int m = mat[0].size(); vector<vector<int> > row(n, vector<int>(m, 0)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (j == 0) { row[i][j] = mat[i][j]; } elseif (mat[i][j]) { row[i][j] = row[i][j - 1] + 1; } else { row[i][j] = 0; } } } int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { int col = row[i][j]; for (int k = i; k >= 0 && col; --k) { col = min(col, row[k][j]); ans += col; } } } return ans; } };
intnumSubmat(int** mat, int matSize, int* matColSize) { int n = matSize; int m = matColSize[0]; int row[n][m]; memset(row, 0, sizeof(row)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (j == 0) { row[i][j] = mat[i][j]; } elseif (mat[i][j]) { row[i][j] = row[i][j - 1] + 1; } else { row[i][j] = 0; } } } int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { int col = row[i][j]; for (int k = i; k >= 0 && col; --k) { col = fmin(col, row[k][j]); ans += col; } } } return ans; }
classSolution: defnumSubmat(self, mat: List[List[int]]) -> int: n, m = len(mat), len(mat[0]) row = [[0] * m for _ inrange(n)] for i inrange(n): for j inrange(m): if j == 0: row[i][j] = mat[i][j] else: row[i][j] = 0if mat[i][j] == 0else row[i][j - 1] + 1 ans = 0 for i inrange(n): for j inrange(m): col = row[i][j] for k inrange(i, -1, -1): col = min(col, row[k][j]) if col == 0: break ans += col return ans
classSolution: defnumSubmat(self, mat: List[List[int]]) -> int: n, m = len(mat), len(mat[0]) row = [[0] * m for _ inrange(n)] for i inrange(n): for j inrange(m): if j == 0: row[i][j] = mat[i][j] else: row[i][j] = 0if mat[i][j] == 0else row[i][j - 1] + 1 ans = 0 for j inrange(m): Q = list() total = 0 for i inrange(n): height = 1 while Q and Q[-1][0] > row[i][j]: # 弹出的时候要减去多于的答案 total -= Q[-1][1] * (Q[-1][0] - row[i][j]) height += Q[-1][1] Q.pop() total += row[i][j] ans += total Q.append((row[i][j], height))