力扣刷题-1371.每个元音包含偶数次的最长子字符串、前缀和、动态规划

2023-10-31

一.背景

  1. 和为k的子数组

    给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

    示例 1 :

    输入:nums = [1,1,1], k = 2
    输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

    来源:力扣(LeetCode)第560道题

  2. 解题思路

    ①.暴力穷举

    ​ 可以对所有的子数组进行穷举,然后分别求和,和等于k则计数加1。

    //穷举所有子串[i,j]
    int countK = 0;
    for(int i = 0;i< nums.size();i++)//穷举所有以i开头的子串
    {
        for(int j = i;j < nums.size();j++)//穷举所有子以j结尾的子串
        {
            int sum = 0;
            for(int k = i;k <= j;k++)//对子串求和
            {
                sum += nums[k];
            }
            if( sum == k)//判断和是否为k
                countk++;
        }
    }
    

    ​ 枚举所有的子串开头 i 和结尾 j,然后确定起始位置后对子串进行从头到尾求和,时间复杂度为O(n3)。

    ②.去除重复计算

    ​ 上述方法中对每个子串进行求和时,都是从头至尾进行累加计算,存在在大量的重复计算。因为在第二层的循环结束时已经得到了[i,j]的和,下次循环求[i,j+1]时则没必要再次从头到尾计算,直接上次的和加上nums[j+1]即可,时间复杂度为O(n2)。

    //穷举所有子串[i,j]
    int countK = 0;
    for(int i = 0;i< nums.size();i++)//穷举所有以i开头的子串
    {
        int sum = 0;
        for(int j = i;j < nums.size();j++)//穷举所有子以j结尾的子串
        {
            sum += nums[j];//上次和的基础上加当前值即可
            if( sum == k)//判断和是否为k
                countk++;
        }
    }
    

    ③.使用前缀和

    ​ 上述方法中就算[i,j+1]的和时,直接用[i,j]的和加上nums[j+1],减少了子串再次从头到尾的计算,根据这一思路,我们定义数组 pre[i] 为[0…i]里所有元素的和:那么 pre[i] = pre[i-1]+nums[i] 。那么要计算任意子区间[i,j]的和,通过pre[j]-pre[i]即可得到。

    int size = nums.size();//数组长度
    vector<int> pre = vector(size,0);//记录[0,i]每个位置的和
    //构造前缀和
    for(int i = 1;i< nums.size();i++)
    {
        pre[i] = pre[i-1]+nums[i];
    }
    //穷举所有子数组
    int countK = 0;
    for(int i = 0;i< nums.size();i++)//穷举所有以i开头的子串
    {
        for(int j = i;j < nums.size();j++)//穷举所有子以j结尾的子串
        {
            if( pre[j] - pre[i] == k)
                countK++;
        }
    }
    

    ​ pre数组作为对数据的预处理只执行一次即可,当我们需要返回任意子数组[i,j]的和时,可以通过 pre[j] - pre[i] O(1) 用的时间得到。

    ​ 但对本题来说,依然需要对数组进行两层遍历,时间复杂度为O(n2)。

    ④.哈希表优化

    ​ 在上述方法中,第二次for循环的作用是:计算有几个 j 能够使 pre[j] 和 pre[i] 的差等于k。但实际上我们不关心前缀和对应哪几项,只需要知道有几个前缀和满足条件即可。因此我们可以用哈希表记录前缀和及该和出现的次数,就可以用O(1)的时间做出判断。

    ​ 哈希表中键值对,键:从0到当前项的总和、值:这个前缀和出现了几次,初始化前缀和为0出现了1次。

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

二.前缀和

  1. 概念

    前缀和是一个数组的某项下标之前(包括该元素)的所有数组元素的和,前缀和是一种重要的预处理操作,可以降低查询的时间复杂度。

  2. 一维前缀和

    一维前缀和主要用于使用O(1)的时间,计算出区间和:nums[i]+nums[i+1]+…+nums[j]

一维前缀和

  1. 二位前缀和

    二维前缀和主要用于对一个矩阵,在O(1)的时间内计算出子矩阵的和。

在这里插入图片描述

在这里插入图片描述

三.应用举例

  1. 统计「优美子数组」

    给你一个整数数组 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;
        }
    };
    
  2. 连续子数组和

    给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 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;     
        }
    };
    
  3. 每个元音包含偶数次的最长子字符串

    给你一个字符串 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++)
            {
                for(int j = 0;j< 5;j++)
                {
                    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;
        }
    };
    
  4. 二维区域和检索-矩阵不可变

    给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。

    上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。

    示例:

    给定 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
    说明:

    你可以假设矩阵不可变。
    会多次调用 sumRegion 方法。
    你可以假设 row1 ≤ row2 且 col1 ≤ col2。

    来源:力扣(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);
     */
    

    在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

力扣刷题-1371.每个元音包含偶数次的最长子字符串、前缀和、动态规划 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • 记一次Redhat7无法正常开机的解决过程

    事情的起源是使用VMWare虚拟平台克隆一个虚拟机的时候 克隆之前将网络配置全部删掉 然后将虚拟机关机 然后克隆出来一台之后 发现两台都无法正常的开机 症状是监视器会显示一个灰色的7背景 然后虽然没有死机但是也无法进入登录窗口 由于克隆之前
  • Failed to restart ssh.service: Unit not found. Centos7不能启动ssh服务

    升级ssh后重启服务 systemctl restart sshd service 遇到报错 Failed to restart ssh service Unit not found 解决方法 执行以下命令即可 进入 etc init d
  • PostgreSQL REPMGR 灾难恢复过程复盘

    大家肯能注意到 最近一直都是各种数据库的灾难恢复的复盘 本身作为一个TEAM 的LEADER 我想到的是在紧急情况下 我们应该有一个应对的措施 对每一个 TEAM 的 DBA 都应该在那个时候沉着冷静 并且知道那些是应该做的 那些是不该做的
  • 记录Mysql使用小技巧

    1 统计用逗号分隔字段中的元素 例如 有如下数据 需要把participants中每个元素出现的次数及对应的id统计出来 id participants 169 吉利 搜狗 1 170 吉利 搜狗 2 171 吉利 3 172 吉利 4 1
  • MySQL的一些基本操作

    现在有的时候线上数据不能直接操作IDE工具 SQL是避免不了的 而且即使是开发也会用到一些语句 将常用的聚集在一起 一 字段 a 表结构修改 1 增加字段 TABLE关键字不能少 ALTER TABLE xxx order ADD orde
  • kali 中msfconsole报警“WARNING: No database support: could not connect to server: Connection refused”及解决

    问题点 kali 2020 02版中msfconsole报警 WARNING No database support could not connect to server Connection refused 解决方法 step1 在终端
  • 126.数据链路层有哪些协议?

    PPP 点到点 HDLC 高级数据链路协议 csma cd carrier sensor multiple access collosion detect 载波多路监听 冲突检测 工作原理 先听后发 边听边发 冲突停发 随机延迟后重发
  • centos7使用rpmbuild制作rpm包

    本文作为我实验的一个总结文档 可能实现的功能比较简单 适合于想要简单入门使用的 希望对朋友们有所帮助 下载rpmbuild程序包 所用系统 centos7 6 yum install rpm build 安装程序包 如果你所要打包的程序需要
  • yum清缓存_YUM 安装及清理

    Yum 全称为 Yellow dog Updater Modified 是一个在Fedora中的Shell前端软件包管理器 基於RPM包管理 能够从指定的服务器自动下载RPM包并且安装 可以自动处理 依赖性关系 并且一次安装所有依赖的软体包
  • 小程序无需编程,体验IoT物联网平台-物模型开发——设备接入类

    微信小程序码 1 准备工作 1 1 注册阿里云账号 浏览器打开 https aliyun com 开通阿里云账号 并通过支付宝实名认证 https www aliyun com gt 1 2 免费开通IoT物联网平台 在产品分类 找到物联网
  • c语言-循环打印星号图形*

    用两层循环 外层循环 控制行 行数 换行 内层循环 控制列 列数 列的符号 第一种效果图 为什么是j lt i 2呢 第一行以0计算 第一行星数为0 第二行为1计算 第二行星数为2 include
  • 【DP练习】美元DOLLARS

    1040 练习题目 美元DOLLARS Description 在以后的若干天里戴维将学习美元与德国马克的汇率 编写程序帮助戴维何时应买或卖马克或美元 使他从100美元开始 最后能获得最高可能的价值 Input 输入文件的第一行是一个自然数
  • Linux TOP CPU %wa 值的理解

    起因 近期阅读到Linux下显示CPU执行情况命令top的使用 网上搜索显示为 单位时间io占用cpu比例 cpu等待输入输出 cpu等待io的时间 起初看来 总觉得是io瓶颈或者是cpu负载率 仔细琢磨 总觉得哪里出了问题 跟进 因为IO
  • 深度优先遍历目录

    磁盘文件系统类型 ext2 ext3 ext4 深度优先遍历目录 include
  • Qt中.pro文件报错问题

    1 error No rule to make target C Program Files x86 Windows Kits 10 Lib 10 0 22621 0 um x64 User32d a needed by debug unt
  • Matlab中特征选择reliefF算法使用方法(分类与回归)

    1 ReliefF简介 ReliefF是特征选择的一种算法 在高维特征样本中 选取部分具有代表性的特征 从而降低样本特征维度 它也是relief算法的进阶 Relief算法只能用来做二分类 但其算法简单 效率高 结果不错 因此才有了其进阶算
  • 超详细!基于Proteus的出租车计价器实现(数字电路课程设计)

    本文阐述基于Proteus 7 8的出租车计价器电路的实现 附具体电路的工程文件下载 工程文件下载链接 设计要求 里程测量精确到1 按起步价7元 3公里 起步价外按1 4元 公里进行计价 等候按1 4元 10分钟计算 具有里程显示 收费显示
  • 浏览器全屏代码

    a href 屏幕切换 a
  • 常用分类算法的优缺点和相关评价指标

    算法 优点 缺点 Bayes 贝叶斯分类法 1 所需估计的参数少 对于缺失数据不敏感 2 有着坚实的数学基础 以及稳定的分类效率 1 假设属性之间相互独立 这往往并不成立 喜欢吃番茄 鸡蛋 却不喜欢吃番茄炒蛋 2 需要知道先验概率 3 分类
  • 力扣刷题-1371.每个元音包含偶数次的最长子字符串、前缀和、动态规划

    一 背景 和为k的子数组 给定一个整数数组和一个整数 k 你需要找到该数组中和为 k 的连续的子数组的个数 示例 1 输入 nums 1 1 1 k 2 输出 2 1 1 与 1 1 为两种不同的情况 来源 力扣 LeetCode 第560