• LeetCode525. 连续数组


    题目说数组长度最长可能到50000,所以如果暴力枚举子数组的起点、终点,再计算数组和,复杂度就是o(n^3),肯定超时。

    可以预处理出前缀和,这样只需要枚举起点和终点即可,但时间复杂度依然是O(n^2), 也不行。

    这里需要一点奇技淫巧,因为数组只包含有0和1,如果一段子数组含有相同数量的0和1,则这个子数组中一半是0,一半是1,那么子数组的和就是这个子数组的长度的一半。

    更进一步,如果我们把数组中所有的0都当作-1处理,那么,当一个子数组中含有相同的0和1(也就是含有相同数量的-1和1)时,我们计算出来的子数组的和就是0了。

    所以,我们可以用一个哈希表存取所有前缀和的下标,这里的前缀和不是直接对原数组计算前缀和,而是把数组中所有0按照-1处理、1仍然按1处理所得到的前缀和。

    哈希表中记录当前前缀和的下标,因为题目要求最长的连续子数组,所以我们只记录当前前缀和的最小的下标,如果之后碰到了一个和当前前缀和相同的前缀和,则这两个下标的差,就是一个含有相同的0和1的子数组的长度,可以用这个长度更新答案。

    上面这几句话有点抽象,实际上就是,假设我们当前得到了一个前缀和x,如果哈希表中没有记录过这个前缀和对应的下标,那么我们在哈希表中记录前缀和x对应的下标(假设为i);之后,如果我们又得到了一个前缀和preSum[j]也是x,则preSum[j] - preSum[i - 1] = 0, 也就是说子数组nums[i ~ j]中含有相同的0和1,因为把所有的0当作-1进行相加之后,得到的子数组的和为0。

    既然得到了满足条件的一个子数组,我们可以用这个子数组的长度更新答案:res = max(res, i - hash[CurPreSum]

    这样,通过用一个哈希表记录前缀和的下标(这里的前缀和计算把原数组中所有的0按照-1处理),我们可以在O(n)的时间复杂度内计算出含有相同数量的0和1的最长连续子数组的长度。

    代码如下:

    class Solution {
    public:
        unordered_map<int, int> hash;                        //  哈希表记录某个前缀和所对应的(最小)下标
    
        int findMaxLength(vector<int>& nums) {
            hash[0] = 0;
            int res = 0;
            int curPreSum = 0;                              // 当前的前缀和
            for(int i = 1; i <= nums.size(); ++i) {
                curPreSum += (nums[i - 1] == 1) ? 1 : -1;      // 0按照-1处理,1仍然是1
                if(hash.find(curPreSum) == hash.end()) {       // 如果之前没有记录过curPreSum的下标,则记录一下
                    hash[curPreSum] = i;
                } else {
                    res = max(res, i - hash[curPreSum]);       // 否则,说明找到了一段满足条件的子数组,用这个子数组的长度更新答案
                }
            }
            return res;
        }
    };
    
  • 相关阅读:
    FPGA quartus开发中常见的错误处理
    verilog中wire与reg类型的区别
    VC++6.0中ClassView中类消失 解决方案[转自网络]
    C++ 和 MFC的学习
    最近单片机编程中的心得
    #ifdef __cplusplus extern "C" { #endif”的定义的含义
    ES6字符串拼接新方法-模板字符串表达式
    JavaScript事件参数对象event
    JavaScript offset家族
    详解JavaScript中的replace()函数
  • 原文地址:https://www.cnblogs.com/linrj/p/13972839.html
Copyright © 2020-2023  润新知