【专题四】前缀和(3)

📝前言说明:

  • 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing

📋本专栏:C++刷题专栏

📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux

🎀CSDN主页 愚润泽

视频


974. 和可被 K 整除的子数组

题目链接:974. 和可被 K 整除的子数组

个人解

思路:

上篇文章的第三题一样,但是,因为哈希表里面可能有多个前缀和都满足要求,而我没想到如何快速搜索到这些前缀和。

暴力解法:O( n 2 n^2 n2)是过不了的。


优质解

思路:

  • 哈希表里面有多个前缀和满足要求是因为:可能出现dp[x2] = dp[x1] + k的情况(即两数之间差nk),导致对于dp[i],可能有多个前缀和与之匹配。那如何保证唯一且能把这些数全部统计到呢?
  • 我们可以在记录前缀和的时候,不记录前缀和,而是记录前缀和 % k的余数。
  • 因为:如果(dp[i] - dp[x]) % k == 0,则dp[i] % k == dp[x] % k
  • 细节问题:我们的数组中有负数,但是C++ 中的取模性质:负数 % 正数 == 负数。所以我们要对模出来的结果进行修正
  • 修正:对于负数结果修正:余数 = dp[i] % k + k,但是为了正负数同一:余数 = (dp[i] % k + k) % k

代码:

cpp 复制代码
class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        unordered_map<int, int> hash;
        int ans = 0, sum = 0;
        hash[0] = 1;
        for(auto x: nums)
        {
            sum += x; // sum代表当前位置的前缀和
            int modulus = (sum % k + k) % k;
            if(hash.count(modulus)) ans += hash[modulus];
            hash[modulus]++;
        }
        return ans;
    }
};

时间复杂度:O( n n n)
空间复杂度:O( n n n)


525. 连续数组

题目链接:525. 连续数组

个人解

思路:

脑子锈了,只能想到O( n 2 n^2 n2)的解法。


优质解

思路:

  • 0-1
  • 问题变成:找子数组所有元素和为0的最长子数组。
  • 细节1:哈希表存储什么? 答:<前缀和,下标>,并且如果遇到相同的前缀和,下标大的不存(因为子数组短)
  • 细节2:当前位置的信息使用完以后才存入哈希表,不然dp[i] - dp[i]这个子数组实际为空
  • 细节3:默认前缀和0的位置要存在-1

代码:

cpp 复制代码
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        int ans = 0, sum = 0;
        unordered_map<int, int> hash;
        hash[0] = -1;
        for(int i = 0; i < nums.size(); i++)
        {
            sum += (nums[i] == 0? -1 : 1);
            if(hash.count(sum)) 
                ans = max(ans, i - hash[sum]);
            else
                hash[sum] = i;
        }
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(n)


1314. 矩阵区域和

题目链接:1314. 矩阵区域和

个人解

这道题和文章中第二题很像,这道题麻烦在下标对应。

思路:

  • 题意:answer中每一格表示的是:以mat[r][c]为中心的,边长为2k+1的正方形中所有元素(且在mat中)的和
  • 建立一个二维前缀和数组:dp[i][j]代表mat[0, 0][i, j]这个矩阵内的元素和
  • 然后利用这个前缀和数组来填写answer
  • 下标对应:初始化时:因为mat的元素是从下标[0, 0]开始的,所以dp[i][j]加的应该是mat[i - 1][j - 1]
  • 使用时:dp[1][1]才对应mat的第一个元素,所以,在使用dp的时候下标都要-1

用时:20:00

屎山代码:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int m = mat.size(), n = mat[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        vector<vector<int>> answer(m, vector<int>(n, 0));
        // 初始化前缀和数组
        for(int i = 1; i <= m; i++)
        {
            for(int j = 1; j <= n; j++)
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
        }

        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                int x1 = max(0, i - k);
                int y1 = max(0, j - k);
                int x2 = min(m - 1, i + k);
                int y2 = min(n - 1, j + k);
                answer[i][j] = dp[x2 + 1][y2 + 1] - dp[x1][y2 + 1] - dp[x2 + 1][y1] + dp[x1][y1];
            }
        }
        return answer;
    }
};

时间复杂度:O( m ∗ n m*n m∗n)
空间复杂度:O( m ∗ n m*n m∗n)


🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
全栈师1 分钟前
C#中分组循环的做法
开发语言·c#
FAREWELL000754 分钟前
C#进阶学习(十六)C#中的迭代器
开发语言·学习·c#·迭代器模式·迭代器
无敌小茶7 分钟前
Linux学习笔记之动静态库
linux·笔记
吗喽对你问好17 分钟前
Java位运算符大全
java·开发语言·位运算
chenglin01625 分钟前
.NET中,const和readonly区别
开发语言·.net
DXM052144 分钟前
牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记3-地图基本操作与实战案例
开发语言·笔记·学习·arcgis·c#·ae·arcgis engine
Vaclee1 小时前
JavaScript-基础语法
开发语言·javascript·ecmascript
CodeWithMe2 小时前
【C++】线程池
开发语言·c++
专注API从业者2 小时前
《Go 语言高并发爬虫开发:淘宝商品 API 实时采集与 ETL 数据处理管道》
开发语言·后端·爬虫·golang
Humbunklung2 小时前
PySide6 GUI 学习笔记——常用类及控件使用方法(常用类矩阵QRectF)
笔记·python·学习·pyqt