diff --git "a/Index/\345\217\214\346\214\207\351\222\210.md" "b/Index/\345\217\214\346\214\207\351\222\210.md" index c2cb643f..60601545 100644 --- "a/Index/\345\217\214\346\214\207\351\222\210.md" +++ "b/Index/\345\217\214\346\214\207\351\222\210.md" @@ -36,7 +36,7 @@ | [1004. 最大连续1的个数 III](https://leetcode-cn.com/problems/max-consecutive-ones-iii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/san-chong-jie-fa-cong-dong-tai-gui-hua-d-gxks/) | 中等 | 🤩🤩🤩 | | [1052. 爱生气的书店老板](https://leetcode-cn.com/problems/grumpy-bookstore-owner/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/grumpy-bookstore-owner/solution/hua-dong-chuang-kou-luo-ti-by-ac_oier-nunu/) | 中等 | 🤩🤩🤩 | | [1221. 分割平衡字符串](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-wumnk/) | 简单 | 🤩🤩🤩🤩 | -| [1332. 删除回文子序列](https://leetcode-cn.com/problems/remove-palindromic-subsequences/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/remove-palindromic-subsequences/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-0zwn/) | 中等 | 🤩🤩🤩🤩 | +| [1332. 删除回文子序列](https://leetcode-cn.com/problems/remove-palindromic-subsequences/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/remove-palindromic-subsequences/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-0zwn/) | 简单 | 🤩🤩🤩🤩 | | [1446. 连续字符](https://leetcode-cn.com/problems/consecutive-characters/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/consecutive-characters/solution/gong-shui-san-xie-jian-dan-shuang-zhi-zh-xtv6/) | 简单 | 🤩🤩🤩🤩🤩 | | [1610. 可见点的最大数目](https://leetcode-cn.com/problems/maximum-number-of-visible-points/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-number-of-visible-points/solution/gong-shui-san-xie-qiu-ji-jiao-ji-he-ti-b-0bid/) | 困难 | 🤩🤩🤩🤩 | | [1743. 从相邻元素对还原数组](https://leetcode-cn.com/problems/restore-the-array-from-adjacent-pairs/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/restore-the-array-from-adjacent-pairs/solution/gong-shui-san-xie-yi-ti-shuang-jie-dan-x-elpx/) | 中等 | 🤩🤩🤩🤩 | diff --git "a/LeetCode/361-370/363. \347\237\251\345\275\242\345\214\272\345\237\237\344\270\215\350\266\205\350\277\207 K \347\232\204\346\234\200\345\244\247\346\225\260\345\200\274\345\222\214\357\274\210\345\233\260\351\232\276\357\274\211.md" "b/LeetCode/361-370/363. \347\237\251\345\275\242\345\214\272\345\237\237\344\270\215\350\266\205\350\277\207 K \347\232\204\346\234\200\345\244\247\346\225\260\345\200\274\345\222\214\357\274\210\345\233\260\351\232\276\357\274\211.md" index 5a87b04b..5a2fa2b8 100644 --- "a/LeetCode/361-370/363. \347\237\251\345\275\242\345\214\272\345\237\237\344\270\215\350\266\205\350\277\207 K \347\232\204\346\234\200\345\244\247\346\225\260\345\200\274\345\222\214\357\274\210\345\233\260\351\232\276\357\274\211.md" +++ "b/LeetCode/361-370/363. \347\237\251\345\275\242\345\214\272\345\237\237\344\270\215\350\266\205\350\277\207 K \347\232\204\346\234\200\345\244\247\346\225\260\345\200\274\345\222\214\357\274\210\345\233\260\351\232\276\357\274\211.md" @@ -6,13 +6,13 @@ Tag : 「二分」、「前缀和」 -给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。 +给你一个 `m x n` 的矩阵 `matrix` 和一个整数 $k$ ,找出并返回矩阵内部矩形区域的不超过 $k$ 的最大数值和。 -题目数据保证总会存在一个数值和不超过 k 的矩形区域。 - -  +题目数据保证总会存在一个数值和不超过 $k$ 的矩形区域。 示例 1: +![](https://assets.leetcode.com/uploads/2021/03/18/sum-grid.jpg) + ``` 输入:matrix = [[1,0,1],[0,-2,3]], k = 2 @@ -28,11 +28,11 @@ Tag : 「二分」、「前缀和」 ``` 提示: -* m == matrix.length -* n == matrix[i].length -* 1 <= m, n <= 100 -* -100 <= matrix[i][j] <= 100 -* -$10^5$ <= k <= $10^5$ +* $m == matrix.length$ +* $n == matrix[i].length$ +* $1 <= m, n <= 100$ +* $-100 <= matrix[i][j] <= 100$ +* -$10^5 <= k <= 10^5$ --- @@ -46,8 +46,8 @@ Tag : 「二分」、「前缀和」 数据范围是 $10^2$,对应的计算量是 $10^8$,理论上会超时,但当我们枚举「矩形左上角」$(i,j)$ 的时候,我们只需要搜索位于 $(i,j)$ 的右下方的点 $(p,q)$ 作为「矩形右下角」,所以其实我们是取不满 $m^2 * n^2$ 的,但仍然具有超时风险(2021/04/20 Java 测试可通过,C++ 使用 `vector` 会 TLE)。 -代码: -```Java [] +Java 代码: +```Java class Solution { public int maxSumSubmatrix(int[][] mat, int k) { int m = mat.length, n = mat[0].length; @@ -74,6 +74,35 @@ class Solution { } } ``` +C++ 代码: +```C++ +int sum[110][110]; +class Solution { +public: + int maxSumSubmatrix(vector>& mat, int k) { + int m = mat.size(), n = mat[0].size(); + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mat[i - 1][j - 1]; + } + } + int ans = INT_MIN; + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + for(int p = i; p <= m; p++){ + for(int q = j; q <= n; q++){ + int cur = sum[p][q] - sum[i - 1][q] - sum[p][j - 1] + sum[i - 1][j - 1]; + if(cur <= k){ + ans = max(ans,cur); + } + } + } + } + } + return ans; + } +}; +``` * 时间复杂度:预处理前缀和数组复杂度为 $O(m * n)$,查找答案的复杂度为 $O(m^2 * n^2)$。整体复杂度为 $O(m^2 * n^2)$。 * 空间复杂度:$O(m * n)$ @@ -149,8 +178,8 @@ $$ 至此,我们通过预处理前缀和 + 容斥原理彻底将题目转化为「一维问题」进行来求解。 -代码: -```Java [] +Java 代码: +```Java class Solution { public int maxSumSubmatrix(int[][] mat, int k) { int m = mat.length, n = mat[0].length; @@ -190,6 +219,38 @@ class Solution { } } ``` +C++ 代码: +```C++ +class Solution { +public: + int maxSumSubmatrix(vector>& mat, int k) { + int m = mat.size(), n = mat[0].size(); + vector> sum(m + 1,vector(n + 1,0)); + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mat[i - 1][j - 1]; + } + } + int ans = INT_MIN; + for(int top = 1; top <= m; top++){ + for(int bot = top; bot <= m; bot++){ + set st; + st.insert(0); + for(int r = 1; r <= n; r++){ + int right = sum[bot][r] - sum[top - 1][r]; + auto left = st.lower_bound(right - k); + if(left != st.end()){ + int cur = right - *left; + ans = max(ans,cur); + } + st.insert(right); + } + } + } + return ans; + } +}; +``` * 时间复杂度:枚举上下边界复杂度为 $O(m^2)$;枚举右边界为 $O(n)$,使用 `TreeSet`(基于红黑树)存储和查找左边界复杂度为 $O(\log{n})$。整体复杂度为 $O(m^2 * n\log{n})$ * 空间复杂度:$O(m * n)$ @@ -201,8 +262,8 @@ class Solution { 事实上,我们需要将「二分过程」应用到数值较大的行或者列之中,这样才能最大化我们查找的效率(同时也回答了本题的进阶部分)。 -代码: -```Java [] +Java 代码: +```Java class Solution { public int maxSumSubmatrix(int[][] mat, int k) { int m = mat.length, n = mat[0].length; @@ -234,6 +295,39 @@ class Solution { } } ``` +C++ 代码: +```C++ +class Solution { +public: + int maxSumSubmatrix(vector>& mat, int k) { + int m = mat.size(), n = mat[0].size(); + vector> sum(m + 1,vector(n + 1,0)); + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mat[i - 1][j - 1]; + } + } + bool isRight = n > m; + int ans = INT_MIN; + for(int i = 1; i <= (isRight ? m : n); i++){ + for(int j = i; j <= (isRight ? m : n); j++){ + set st; + st.insert(0); + for(int fixed = 1; fixed <= (isRight ? n : m); fixed++){ + int a = isRight ? sum[j][fixed] - sum[i - 1][fixed] : sum[fixed][j] - sum[fixed][i - 1]; + auto b = st.lower_bound(a - k); + if(b != st.end()){ + int cur = a - *b; + ans = max(ans,cur); + } + st.insert(a); + } + } + } + return ans; + } +}; +``` * 时间复杂度:预处理「每行」或「每列」的前缀和,复杂度为 $O(m * n)$;枚举子矩阵的「上下行」或「左右行」,复杂度为 $O(\min(m, n)^2)$;结合二维前缀和套用一维最大连续子数组解决方案,复杂度为$\max(m, n)\log{\max(m, n)}$。整体复杂度为 $O(\min(m, n)^2 * \max(m, n)\log{\max(m, n)})$ * 空间复杂度:$O(m * n)$ @@ -245,8 +339,8 @@ class Solution { 因此我们可以将计算前缀和的逻辑下放到搜索子矩阵的循环里去做,从而将 $O(m * n)$ 的空间复杂度下降到 $O(\max(m,n))$。 -代码: -```Java [] +Java 代码: +```Java class Solution { public int maxSumSubmatrix(int[][] mat, int k) { int m = mat.length, n = mat[0].length; @@ -275,6 +369,37 @@ class Solution { } } ``` +C++ 代码: +```C++ +class Solution { +public: + int maxSumSubmatrix(vector>& mat, int k) { + int m = mat.size(), n = mat[0].size(); + bool isRight = n > m; + vector sum((isRight ? n + 1 : m + 1), 0); + int ans = INT_MIN; + for(int i = 1; i <= (isRight ? m : n); i++){ + fill(sum.begin(),sum.end(),0); + for(int j = i; j <= (isRight ? m : n); j++){ + set st; + st.insert(0); + int a = 0; + for(int fixed = 1; fixed <= (isRight ? n : m); fixed++){ + sum[fixed] += isRight ? mat[j - 1][fixed - 1] : mat[fixed - 1][j - 1]; + a += sum[fixed]; + auto b = st.lower_bound(a - k); + if(b != st.end()){ + int cur = a - *b; + ans = max(ans,cur); + } + st.insert(a); + } + } + } + return ans; + } +}; +``` * 时间复杂度:预处理「每行」或「每列」的前缀和,复杂度为 $O(m * n)$;枚举子矩阵的「上下行」或「左右行」,复杂度为 $O(\min(m, n)^2)$;结合二维前缀和套用一维最大连续子数组解决方案,复杂度为$\max(m, n)\log{max(m, n)}$。整体复杂度为 $O(\min(m, n)^2 * \max(m, n)\log{\max(m, n)})$ * 空间复杂度:$O(\max(m, n))$ @@ -282,11 +407,11 @@ class Solution { ### 最后 -这是我们「刷穿 LeetCode」系列文章的第 `No.363` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。 +这是我们「刷穿 LeetCode」系列文章的第 `No.363` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 -为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode。 +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 diff --git "a/LeetCode/611-620/611. \346\234\211\346\225\210\344\270\211\350\247\222\345\275\242\347\232\204\344\270\252\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/611-620/611. \346\234\211\346\225\210\344\270\211\350\247\222\345\275\242\347\232\204\344\270\252\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" index 7004549b..f6f43a9d 100644 --- "a/LeetCode/611-620/611. \346\234\211\346\225\210\344\270\211\350\247\222\345\275\242\347\232\204\344\270\252\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" +++ "b/LeetCode/611-620/611. \346\234\211\346\225\210\344\270\211\350\247\222\345\275\242\347\232\204\344\270\252\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -21,8 +21,8 @@ Tag : 「排序」、「二分」、「双指针」 2,2,3 ``` 注意: -1. 数组长度不超过1000。 -2. 数组里整数的范围为 [0, 1000]。 +1. 数组长度不超过 $1000$。 +2. 数组里整数的范围为 $[0, 1000]$。 ---