📌  相关文章
📜  最长子数组,其元素可以通过最大K增量来相等(1)

📅  最后修改于: 2023-12-03 15:10:37.746000             🧑  作者: Mango

最长子数组,其元素可以通过最大K增量来相等

本题目中,给定一个数组和一个整数K,求该数组中最长的子数组,使得子数组中任意两个元素之间的差值不超过K。

解法

一个很直观的思路是,对于每一个元素,向前或向后寻找最长的合法子数组,然后更新答案。但是这样的时间复杂度显然会很高($O(n^2)$)。

考虑将这个问题转化一下,将每个元素加上或减去至多K,使得最终的数组中所有元素相等。显然,最终数组的元素值一定是原数组的某个元素加上至多K或减去至多K得到的。因此只需要枚举数组中的每个元素,然后利用map记录元素值的出现次数,从而通过计算map中任意两个key之间的距离,得到以该元素为结尾的最长子数组。

具体来说,设当前枚举到的元素为$x$,当前最长子数组的起始下标为$i$,$map$表示元素值的出现次数,则更新最长子数组的步骤如下:

  1. 将$x$加上或减去至多K,得到一个合法的元素值$y$。
  2. 在$map$中寻找最大的key$p$,满足$y\geq p$且$y-p\leq K$,并设其出现次数为$c$。
  3. 如果有多组$p,c$满足条件,则选择使得$c$最大的那一组$p,c$。
  4. 更新$i$为$max{ i,p}$,更新答案为$max{ ans,i-x+1}$。
  5. 将$y$加入$map$。

由于每个元素值只会在map中被加入或删除一次,因此总时间复杂度为$O(nlogn)$。

代码
int longestSubarray(vector<int>& nums, int limit) {
    int ans=1,n=nums.size();
    map<int,int> mp;
    int l=0,r=0;
    mp[nums[0]]++;
    while(r<n){
        int maxn = mp.rbegin()->first,cnt = mp.rbegin()->second;
        int minn = mp.begin()->first;
        if(maxn-minn <= limit) ans=max(ans,r-l+1),r++;
        else if(--mp[nums[l]]==0) mp.erase(nums[l]),l++;
        if(r<n&&mp.count(nums[r])!=0) mp[nums[r]]++;
        else if(r<n){
            while(mp.size()){
                if(abs(mp.begin()->first - nums[r]) <= limit){
                    mp[nums[r]]++,r++;
                    break;
                }
                if(--mp[nums[l]]==0) mp.erase(nums[l]),l++;
            }
        }
    }
    return ans;
}

注意:由于map中需要维护元素值的出现次数,因此需要检查$mp.count(nums[r])$是否为0,而不是简单地使用$mp[nums[r]]$。