Given a matrix
and a target
, return the number of non-empty submatrices that sum to target.
A submatrix x1, y1, x2, y2
is the set of all cells matrix[x][y]
with x1 <= x <= x2
and y1 <= y <= y2
.
Two submatrices (x1, y1, x2, y2)
and (x1', y1', x2', y2')
are different if they have some coordinate that is different: for example, if x1 != x1'
.
Example 1:
Input: matrix = [[0,1,0],[1,1,1],[0,1,0]], target = 0
Output: 4
Explanation: The four 1x1 submatrices that only contain 0.
Example 2:
Input: matrix = [[1,-1],[-1,1]], target = 0
Output: 5
Explanation: The two 1x2 submatrices, plus the two 2x1 submatrices, plus the 2x2 submatrix.
Example 3:
Input: matrix = [[904]], target = 0
Output: 0
Constraints:
1 <= matrix.length <= 100
1 <= matrix[0].length <= 100
-1000 <= matrix[i] <= 1000
-10^8 <= target <= 10^8
這道題給了一個二維矩陣 matrix 和一個整型數 target,問有多少個非空的子矩陣使得其和正好等於 target。首先來想,這道題身為 Hard 難度,肯定不能用暴力搜索,博主之前也屢次強調過,這是一種不尊重,會慘遭 OJ 的毒打。既然是要求子矩陣之和的問題,就來想想有沒有什么方法可以快速求得子矩陣之和,博主馬上就聯想到了之前在求子數組之和時,可以通過建立累加和數組來快速的求出任意的子數組之和。這里也可以利用相同的思路,只不過是建立的是累加和矩陣,從一維升級到了二維而已,為的就是以空間來換取時間。建立的方法比一維的稍稍復雜一些,大小為 (m+1)x(n+1)
,多出一位可以避免越界,然后從1開始遍歷,當前位置的累加和等於上面的值加上左邊的值減去左上方的值,最后加上原數組中當前位置的值,這樣整個累加和矩陣就建立好了。接下來就要遍歷所有子矩陣了,由於矩陣有四個端點,所以遍歷是四次方的時間復雜度,不過好在有累加和矩陣,只要四個端點坐標確定了,可以在常數級的時間復雜度內求出子矩陣之和,若這個值等於 target,則結果 res 自增1即可。這個方法基本上是壓線過的 OJ,多虧 OJ 仁慈,放了一馬,參見代碼如下:
解法一:
class Solution {
public:
int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
int res = 0, m = matrix.size(), n = matrix[0].size();
vector<vector<int>> sums(m + 1, vector<int>(n + 1));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
sums[i][j] = sums[i][j - 1] + sums[i - 1][j] - sums[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
for (int p = 1; p <= i; ++p) {
for (int q = 1; q <= j; ++q) {
int t = sums[i][j] - sums[i][q - 1] - sums[p - 1][j] + sums[p - 1][q - 1];
if (t == target) ++res;
}
}
}
}
return res;
}
};
上面方法其實沒有太多的技巧,硬說是 Hard 的難度有些牽強,再來看一種論壇上的高分解法,一種真正符合其身價的解法。這種解法的思路在之前的 Subarray Sum Equals K 和 Max Sum of Rectangle No Larger Than K 其實都出現過,有點 Two Sum 的影子在里面。話說 Two Sum 可是博主刷的第一道題呢,那個一個陽光明媚的下午,博主安靜地坐在圖書館中,打開了心愛的小 Mac,登進了朋友推薦的 LeetCode 網站,從此便和力扣結下了不解之緣。如今已經刷了上千道題了,成了大家眼中的千條哥了。好了,打住打住,回到題目,具體的思路是,先建立每一行的累加和數組,這里其實將矩陣微積分化了,看作是多行的累加,有了行的累加和數組,就可以快速知道每一行的子數組之和了。接下來只要確定行的寬度就行了,即遍歷任意兩個列,它們之間的距離就是子矩陣的寬,然后新建一個 HashMap,建立子矩陣之和跟其出現次數之間的映射,初識時將 0->1
這個映射對兒加入,后面會講原因。然后新建一個變量 cur,接下來遍歷所有行,由於子矩陣的寬已經確定了,遍歷不同行,就是進一步確定子矩陣的高,這樣就能准確的確定一個子矩陣的范圍了。先利用行的累加和數組來快速求出該行的數字之和,注意為了避免數組越界,需要判斷一下i是否大於0。當前的子矩陣之和求出來后,保存在了 cur 之中,現在要看其和 target 之間的關系,cur 如果小於 target,則無事發生;若大於 target,則看 cur - target 是否存在,若存在,則表示和為 target 的子矩陣也必然存在,且個數跟和為 cur-target 的子矩陣相同,所以結果 res 可以直接加上 cur-target 的映射值。但是還有一種情況,當 cur 正好等於 target 的時候,cur-target 就為0了,這時候 HashMap 中0的映射值若為0,則就沒法加上這種情況了,這就是為啥要將 0->1
這個映射對兒提前加入的原因。最后別忘了將 cur 的映射值自增1,參見代碼如下:
解法二:
class Solution {
public:
int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
int res = 0, m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 1; j < n; ++j) {
matrix[i][j] += matrix[i][j - 1];
}
}
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
unordered_map<int, int> cntMap{{0, 1}};
int cur = 0;
for (int k = 0; k < m; ++k) {
cur += matrix[k][j] - (i > 0 ? matrix[k][i - 1] : 0);
res += cntMap[cur - target];
++cntMap[cur];
}
}
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1074
類似題目:
Max Sum of Rectangle No Larger Than K
參考資料:
https://leetcode.com/problems/number-of-submatrices-that-sum-to-target/