青少年人工智能编程科普教育机构-凡恩机器人联盟

手机站
当前位置: 主页 > C++ > 正文

少儿编程C++基础:前缀和算法(二)-成都凡恩机器人联盟

来源:成都凡恩
发布人:青少年编程教育
时间:2024-11-12 15:33:58

前缀和是一个数组的某项下标之前(包括该元素)的所有数组元素的和,前缀和是一种重要的预处理操作,可以降低查询的时间复杂度。
本文以 leetcode 中算法题为例子多前缀和算法的应用进行说明

前缀和应用举例

01、统计「优美子数组」

给你一个整数数组 nums 和一个整数 k。如果某个 连续 子数组中恰好有 k
个奇数数字,我们就认为这个子数组是「优美子数组」。请返回这个数组中「优美子数组」的数目。示例 1:输入:nums = [1,1,2,1,1], k = 3
输出:2 解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1]

力扣(LeetCode)第1248道题

同求连续子区间的和,只不过改为了连续子区间的奇数数字个数,同样可以使用前缀和的思路求解。

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        unordered_map<int,int> mp;//记录 奇数个数-次数
        mp[0] = 1;
        int count = 0,pre = 0;//仅需要记录前一个值即可,不需要用数组记录所有的前缀和
            for(int i = 0;i< nums.size();i++)      
            {            
                if( nums[i] % 2 == 1)               
                    pre++;//计算前缀和            
                if(mp.find(pre-k) != mp.end())//前面结果中存在满足条件的前缀和            
                {               
                    count+=mp[pre-k];            
                }            
                mp[pre]++;        
            }        
            return count;
    }
};

02、连续子数组和

给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 的倍数,即总和为 n*k,其中 n
也是一个整数。示例 1: 输入: [23,2,4,6,7], k = 6 输出: True 解释: [2,4] 是一个大小为 2 的子数组,并且和为 6。

力扣(LeetCode)第523道题

求连续子数组的和可以采用前缀和的思路。区间[i,j]的和为k的倍数,即求 nums[j] – nums[i] 差为k的倍数,只要 nums[j] 和
nums[i] 除以k的余数相等即满足需求,因此只要记录每个位置前缀和除以k的余数即可。

本题只需要判断是否存在,对于每个余数只需要记录第一次出现位置即可,初始化前缀和为0的位置下标为-1。

当k = 0时,只要存在两个前缀和相等,则区间和为0,同样满足要求。

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        unordered_map<int,int> pre_map;//记录 前缀和%k - 首次位置
        int sum = 0;
        pre_map[0]=-1;//初始化前缀和为0的下标为-1
        for(int i = 0;i < nums.size();i++)
        {
            sum += nums[i];//前缀和
            if( k != 0)
            {
                sum = sum%k;//对k求余数
            }
           if( pre_map.find(sum) != pre_map.end())//存在满足条件的前缀和            
           {                
               if( i - pre_map[sum] > 1)//判断长度大于1                    
                   return true;            
           }           
           else            
           {                
               pre_map.insert({sum,i});            
           }
        }        
        return false;
    }
};

03、每个元音包含偶数次的最长子字符串

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,’e’,’i’,’o’,’u’
,在子字符串中都恰好出现了偶数次。示例 1:输入:s = “eleetminicoworoep” 输出:13 解释:最长子字符串是
“leetminicowor” ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。

力扣(LeetCode)第1371道题

在题1248统计「优美子数组」中,统计连续子区间奇数数字出现的次数,本题改为了多个字符出现的次数,同样适用前缀和的思路;在题523连续子数组和中,条件是前缀和是k的整数倍,本题中条件为偶数次,即前缀和是2的整数倍,即两个位置对2求余的结果相等即可。

  • 用二进制数字记录状态:我们可以对每个元音字母都维护一个前缀和的数组,实际上我们数组中保存的是每个前缀和对2的余数,即0和1,因此没必要真的维护5个数组,使用一个二进制数字即可表示当前的组合状态,二进制数字中一位代表一个元音字母的状态。比如 00000表示每个元音字母都没出现。本题需求转换为找两个位置的二进制状态相等。

  • 用异或翻转二进制位:用二进制位表示从开始到当前位置的状态,未出现标记成0,出现一次标记为1,再次出现则翻转为0,即某一个位对1进行异或的结果。

  • 哈希表优化:本题中需求为找两个位置的状态相同,并且距离最远。只需要记录每个位置首次出现的位置即可,不断的找相同的状态,越靠后出现的离的越远。

    class Solution {
    public:
    int findTheLongestSubstring(string s) {
    int res = 0;//保存返回结果
    s = '#'+s;//前置添加一个固定字符,初始化-1的位置
    unordered_map<int,int> mp;//每种状态首次出现的位置
    string str_temp = "aeiou";//同二进制数字中位的位置
    int pre = 0;//前缀状态,初始化为00000
    for(int i = 0;i < s.length();i++)
    {
    if( s[i] == str_temp[j])
    pre = pre^(1 << (4 - j)) ;//对相应位置的状态和1进行异或,0变0、0变1
    }
    if( mp.find(pre) != mp.end())//状态相等
    {
    res = max(res,i-mp[pre]);//更新最远距离
    }
    else
    {
    mp[pre] = i;//记录首次出现的位置
    }
    }

        return res;    
    }

    };

04、二维区域和检索-矩阵不可变

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。示例: 给定
matrix = [ [3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7],
[1, 0, 3, 0, 5] ] sumRegion(2, 1, 4, 3) -> 8 sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

力扣(LeetCode)第304题

先构造前缀和,然后可以O(1)计算给出任意子矩阵的和。

class NumMatrix {
    public:
    vector<vector<int>> presum;//记录前缀和
    int rowcount =0;    
    int colcount = 0; 

    NumMatrix(vector<vector<int>>& matrix) {
        rowcount = matrix.size();
        if( rowcount  == 0 ) 
            return;
        else 
            colcount = matrix[0].size();
        if(colcount == 0 ) return;

        presum = vector(rowcount,vector(colcount,0));//初始化前缀和数组       
        //构造前缀和数组,presum[i][j] = nums[i][j] + presum[i-1][j] + presum[i][j-1] - presum[i-1][j-1]

        for(int i =0;i < rowcount;i++)        
        {            
            for(int j = 0;j < colcount;j++)            
            {                
                presum[i][j] = matrix[i][j];                
                if( i-1 >= 0) presum[i][j] += presum[i-1][j] ;                
                if( j-1 >= 0) presum[i][j] += presum[i][j-1] ;                
                if( i-1>= 0 && j-1 >= 0) presum[i][j] -= presum[i-1][j-1] ;            
             }        
        }
    }
    int sumRegion(int row1, int col1, int row2, int col2) 
    {
        if(rowcount == 0 || colcount == 0) return 0;
        //子矩阵和公式:presum[row2][col2] - presum[row1-1][col2] - presum[row2][col1-1] +presum[row1-1][col1-1]
        int res = presum[row2][col2];        
        if( row1 -1 >=0) res -= presum[row1-1][col2];        
        if( col1 -1 >=0) res -= presum[row2][col1-1];
        if(row1 -1 >= 0 && col1-1 >=0 )
            res += presum[row1-1][col1-1];

        return res;
    }
};


/** 
*Your NumMatrix object will be instantiated and called as such: * 
NumMatrix* obj = new NumMatrix(matrix); int param_1 = obj->sumRegion(row1,col1,row2,col2);
声明:本站稿件凡恩品牌资讯以外部分类目资讯转载来自于互联网,如有疑义请联系我们删除。
10年以上业内强师集结,手把手带你蜕变精英
请您保持通讯畅通,专属学习老师24小时内将与您1V1沟通
免费领取
相关推荐HOT
少儿编程C++基础:前缀和算法(一)
少儿编程C++基础:前缀和算法(一)

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。示例 1 : 输入:nums = [1,1,1], k = 2 输出: 2, [1,1] 与 [1,1] 为两种不同的......详情>>

2024-11-12
少儿编程C++基础:双指针使用总结(三)
少儿编程C++基础:双指针使用总结(三)

双指针是一种解决问题的技巧或者思维方式,指在访问一个序列中的数据时使用两个指针进行扫描,两个指针可以是同向的,也可以是反向的;我们的关注点可以是这两个指针指向的两个元素本身,也可以是两个指针中间的区域......详情>>

2024-11-12
少儿编程C++基础:双指针总结(一)
少儿编程C++基础:双指针总结(一)

双指针是一种解决问题的技巧或者思维方式,指在访问一个序列中的数据时使用两个指针进行扫描,两个指针可以是同向的,也可以是反向的;我们的关注点可以是这两个指针指向的两个元素本身,也可以是两个指针中间的区域......详情>>

2024-11-12
少儿编程C++基础:图的表示方式
少儿编程C++基础:图的表示方式

图的概念1、基本术语图是由节点以及连接这些节点边组成。2、应用举例2.1社交网络在社交网络中所有的用户构成了多对多的朋友关系网,这个关系网就是图:每个人都是图中的节点,互相认识的人之间通过边进行联系。......详情>>

2024-11-12
少儿编程C++基础:双指针的使用总结(二)
少儿编程C++基础:双指针的使用总结(二)

双指针是一种解决问题的技巧或者思维方式,指在访问一个序列中的数据时使用两个指针进行扫描,两个指针可以是同向的,也可以是反向的;我们的关注点可以是这两个指针指向的两个元素本身,也可以是两个指针中间的区域......详情>>

2024-11-12
少儿编程导师
朵朵老师
少儿编程导师
琴琴老师
少儿编程导师
苏老师

赛事资讯

凡恩资讯

政策资讯