当前位置: 首页 > news >正文

前缀和以及哈希表优化

一、引入

最近在刷题的时候,遇到了一些前缀和结合子数组的题目,这些题目,有一个共同的特性,就是说,刚开始都能够想利用前缀和进行优化,然后再结合子序列的左右端点进行枚举,最后确定哪一个区间或者哪一些是能够满足我们的题目要求,但是有一个问题就是,我们在枚举区间的两个端点的时间复杂度是 O ( n 2 ) O(n^2) O(n2)的,所以一旦题目数据给的特别大的时候,就会过不了,这个时候就需要利用题目的相关性质结合哈希表解题,把时间复杂度降到 O ( n ) O(n) O(n)以下,就可以过了,这里整理了相关类似的题目,此专题也会持续的更新…

二、题目汇总

①力扣1124.表现良好的最长时间段

原题(来源于leetcode)

暴力分析

①第一种暴力的想法,就是枚举左右端点,再通过一层循环枚举每一个左右端点形成的区间的表现良好和不良好的时间段的个数,那么一旦符合良好的个数大于我们的不良好的个数,就可以更新我们的答案了,时间复杂度为O(n^3),肯定是过不了的。

②显然我们利用前缀和可以优化一层找良好和不良好时间段的个数,记良好的为1,不良好的为-1,那么一段区间的区间和大于0就说明良好严格大于不良好,那么最终的时间复杂度为 O ( n 2 ) O(n^2) O(n2)的,如果n比较小,那么这个方式是可以过的。

优化分析

由于暴力过不去,那么此时我们想的问题应该是,如何不直接两层循环枚举左右端点就可以求出我们的最终结果呢?

提示1

其实这个地方可以说利用了贪心的一个思想,因为题目很明确的说了,求的是最大的子区间长度,那么我们其实不妨,枚举的时候从右往左枚举,对于一个左端点,一旦右端点从最右往左到某一个位置恰好满足题意的话,那么我们就可以不用继续让右端点往左了,因为已经找到一个可能为答案的备选最大值。

提示2

整个问题变成,如何找一段区间长度和大于0,且这个区间的长度是求最大值。我们只需要记录可能成为左端点的下标,并且满足一旦某个点的前缀和成为了我们左端点的备选点后,在他右边所有和他等值的点都不会成为我们的左端点备选点。

提示3

备选点从左到右一定是严格单调下降的,在实现遍历的时候便是利用单调栈的思想。

提示4

这里有一篇相关题解,里面有比较详细的说明。

完整AC代码

class Solution {
public:
    int longestWPI(vector<int>& hours) {
        int n = hours.size();
        vector<int> pre(n + 1, 0);
        for(int i = 1; i <= n; i ++ ) {
            auto now = hours[i - 1];
            if(now > 8) pre[i] = 1;
            else pre[i] = -1;
            pre[i] += pre[i - 1];
        }
        stack<int> stk;
        int ans = 0;
        for(int i = 0; i <= n; i ++ ) {
            if(stk.empty() || pre[stk.top()] > pre[i]) stk.push(i);
        }

        for(int i = n; i >= 0; i -- ) {
            if(stk.empty()) break;
            if(!stk.empty() && i <= stk.top()) stk.pop();
            while(!stk.empty() && pre[i] > pre[stk.top()]) {
                ans = max(ans, i - stk.top());
                stk.pop();
            }
        }

        return ans;
    }
};

②力扣525.连续数组

原题(来源于leetcode)

暴力分析

同上一题的分析,可以利用最暴力和一个前缀和优化的方法,时间复杂度分别为 O ( n 3 ) 和 O ( n 2 ) O(n^3)和O(n^2) O(n3)O(n2)

优化分析

提示1

把所有的0变成-1,利用前缀和,只需要求哪一些区间的区间和为0就好。

提示2

利用哈希表记录第一次出现前缀和为x的下标位置。

提示3

一旦遍历的时候遇到前缀和为x,那么如果前面也出现过前缀和为x的下标,直接利用两个下标差更新答案就好。

完整AC代码

class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        int n = nums.size();
        vector<int> pre(n + 1, 0);
        for(int i = 1; i <= n; i ++ ) {
            auto now = nums[i - 1];
            if(now == 0) pre[i] = -1;
            else pre[i] = 1;
            pre[i] += pre[i - 1]; 
        }
        map<int, int> mp;
        mp[0] = 0;
        int ans = 0;
        for(int i = 1; i <= n; i ++ ) {
            auto t = pre[i];
            if(mp.count(t)) {
                ans = max(ans, i - mp[t]);
            }
            else {
                mp[t] = i;
            }
        }

        return ans;
    }
};

③力扣560.和为K的子数组

原题(来源于leetcode)

暴力分析

同第一题的分析,可以利用最暴力和一个前缀和优化的方法,时间复杂度分别为 O ( n 3 ) 和 O ( n 2 ) O(n^3)和O(n^2) O(n3)O(n2)

优化分析

提示1

利用前缀和快速计算区间长度。

提示2

利用哈希表,记录在遍历过的前缀和出现的次数。

完整AC代码

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> pre(n + 1, 0);
        for(int i = 1; i <= n; i ++ ) {
            pre[i] = nums[i - 1];
            pre[i] += pre[i - 1];
        }
        map<int, int> mp;
        mp[0] = 1;
        int cnt = 0;
        for(int i = 1; i <= n; i ++ ) {
            auto now = pre[i], last = pre[i] - k;
            cnt += mp[last];
            mp[now] ++ ;
        }
        return cnt;
    }
};

④力扣1371.每个元音包含偶数次的最长字符串长度

原题(来源于leetcode)

暴力分析

同前

优化分析

提示1

把元音的使用情况,进行二进制压缩。

提示2

出现检验奇偶个数,利用异或求解。且奇数-奇数为偶数,偶数-偶数为偶数。

完整AC代码

class Solution {
public:
    int findTheLongestSubstring(string s) {
        int ans = 0, n = s.size();
        vector<int> mp(1 << 5, -2);
        mp[0] = -1;
        int state = 0;
        for(int i = 0; i < n; i ++ ) {
            auto t = s[i];
            if(t == 'a') {
                state ^= (1 << 0);
            }
            else if(t == 'e') {
                state ^= (1 << 1);
            }
            else if(t == 'i') {
                state ^= (1 << 2);
            }
            else if(t == 'o') {
                state ^= (1 << 3);
            }
            else if(t == 'u') {
                state ^= (1 << 4);
            }
            if(mp[state] != -2) {
                ans = max(ans, i - mp[state]);
            }
            else {
                mp[state] = i;
            }
        }
        return ans;
    }
};

相关文章:

  • 【JavaSE】多线程篇(一)线程的相关概念与线程的基本使用
  • 8、学习 Java 中的方法(方法的定义、可变参数、参数的传递问题、方法重载、方法签名)通过官方教程
  • 数据库基本结论
  • Django-(3)
  • HyperLynx(十五)多板仿真
  • ElasticSearch(四):ES nested嵌套文档与父子文档处理
  • java 基于springboot员工实训项目管理系统
  • SaaS行业的六大安全问题
  • Geoserver+Cesium 发布带样式矢量数据
  • 【C语言】数据类型、存储类
  • 免关注阅读CSDN博客和复制代码(2022.9.1)
  • shell脚本(四)处理用户输入
  • 08 SpringMVC跨域请求
  • Mac下根目录和home目录的区别
  • 猿创征文|opencv对滤波的处理
  • Angularjs之国际化
  • angular组件开发
  • CentOS 7 防火墙操作
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • js中forEach回调同异步问题
  • Linux快速复制或删除大量小文件
  • Rancher如何对接Ceph-RBD块存储
  • Shell编程
  • vagrant 添加本地 box 安装 laravel homestead
  • webpack+react项目初体验——记录我的webpack环境配置
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 算法系列——算法入门之递归分而治之思想的实现
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 问题之ssh中Host key verification failed的解决
  • 学习笔记TF060:图像语音结合,看图说话
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • ###项目技术发展史
  • #mysql 8.0 踩坑日记
  • (04)odoo视图操作
  • (10)ATF MMU转换表
  • (二)JAVA使用POI操作excel
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (转)C#调用WebService 基础
  • (转)大道至简,职场上做人做事做管理
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .cfg\.dat\.mak(持续补充)
  • .NET 反射 Reflect
  • :=
  • @Bean, @Component, @Configuration简析
  • @FeignClient 调用另一个服务的test环境,实际上却调用了另一个环境testone的接口,这其中牵扯到k8s容器外容器内的问题,注册到eureka上的是容器外的旧版本...
  • [AAuto]给百宝箱增加娱乐功能
  • [Android] Android ActivityManager
  • [Bzoj4722]由乃(线段树好题)(倍增处理模数小快速幂)
  • [Docker]十.Docker Swarm讲解
  • [Google Guava] 1.1-使用和避免null
  • [iOS]iOS获取设备信息经常用法
  • [LeeCode]—Wildcard Matching 通配符匹配问题