【算法深练】分组循环:“分”出条理,化繁为简

news2025/7/24 3:52:13

目录

引言

分组循环

2760. 最长奇偶子数组

1446. 连续字符

1869. 哪种连续子字符串更长

2414. 最长的字母序连续子字符串的长度

3456. 找出长度为 K 的特殊子字符串

1957. 删除字符使字符串变好

674. 最长连续递增序列

978. 最长湍流子数组

2110. 股票平滑下跌阶段的数目

228. 汇总区间

1887. 使数组元素相等的减少操作次数

845. 数组中的最长山脉

2038. 如果相邻两个颜色均相同则删除当前颜色

2900. 最长相邻不相等子序列 I

3011. 判断一个数组是否可以变为有序

1578. 使绳子变成彩色的最短时间

1759. 统计同质子字符串的数目

1839. 所有元音按顺序排布的最长子字符串

2765. 最长交替子数组

3255. 长度为 K 的子数组的能量值 II

3350. 检测相邻递增子数组 II

3105. 最长的严格递增或递减子数组

拓展 (加餐)

838. 推多米诺

467. 环绕字符串中唯一的子字符串

3499. 操作后最大活跃区段数 I

2593. 标记所有元素后数组的分数

2948. 交换得到字典序最小的数组

总结


引言

分组循环可以归为同向双指针这一类题目,不同的是将双指针优化为单指针并记录每组的起始位置。

适用场景:按照题目要求,数组会被分割成若干组,每一组的判断/处理逻辑是相同的。

核心思想

  • 外层循环负责遍历组之前的准备工作(记录开始位置),和遍历组之后的统计工作(更新答案最大值)。
  • 内层循环负责遍历组,找出这一组最远在哪结束。

PS:本篇博客中的所有题目均来自于灵茶山艾府 - 力扣(LeetCode)分享的题单。 

分组循环

2760. 最长奇偶子数组

 题解:此题不再是只有一个起点,而是多起点;找出满足题意的最长子数组;外层循环即可起始位置,内层循环找该子数组满足题意的最长位置。

class Solution {
public:
    int longestAlternatingSubarray(vector<int>& nums, int threshold) {
        //找到最长数组,数组满足两个条件:1)奇偶交替;2)子数组最大值不超过threhold
        int i=0,n=nums.size();
        int ret=0;
        while(i<n)
        {
            while(i<n&&(nums[i]%2!=0||nums[i]>threshold)) i++;   //先找到满足题意的第一个位置
            int start=i;   //记录起始位置
            while((i<n&&nums[i]<=threshold)&&(i==start||nums[i]%2!=nums[i-1]%2)) i++; //找出当前子数组的最长长度

            ret=max(ret,i-start);  //更新答案
        }
        return ret;
    }
};

1446. 连续字符

题解:经典的分组循环题目,从多个字符起点开始,找出最长的相同子字符串的长度。

class Solution {
public:
    int maxPower(string s) {
        int n=s.size();
        int i=0,ret=0;
        while(i<n)
        {
            int start=i;  //记录当前位置
            while(i<n&&s[i]==s[start]) i++;  //判断该子字符串有多少个相同的字符

            ret=max(ret,i-start);  //更新答案
        }
        return ret;
    }
};

1869. 哪种连续子字符串更长

题解:与上一题一样,但是此题有两种字符需要进行记录;因此可以直接使用两个整形对两种字符的最长长度进行记录即可。

class Solution {
public:
    bool checkZeroOnes(string s) {
        //分别记录1和0的最长字符串的长度
        int n=s.size();
        int i=0;
        int one=0,zero=0;
        while(i<n)
        {
            int start=i;  //记录起始位置
            while(i<n&&s[i]==s[start]) i++;

            if(s[start]=='0') zero=max(zero,i-start);  //判断对0还是1进行更新
            else one=max(one ,i-start);
        }
        return one>zero;
    }
};

2414. 最长的字母序连续子字符串的长度

题解:分组循环,只是子数组判断条件不同,要求字母序连续。

class Solution {
public:
    int longestContinuousSubstring(string s) {
        //字母序连续
        int n=s.size();
        int i=0,ret=0;
        while(i<n)
        {
            int start=i;  //记录起始位置
            while(i==start||s[i]-s[i-1]==1) i++;   //判断是不是连续字母序

            ret=max(ret,i-start);  //更新答案
        }
        return ret;
    }
};

3456. 找出长度为 K 的特殊子字符串

题解:不需要控制长为K区间来判断时候满足条件,可以直接找出每一段字符串中相同字符的个数,判断该长度是否是K即可。

class Solution {
public:
    bool hasSpecialSubstring(string s, int k) {
        //找长度为K的相同字符串的长度
        int n=s.size();
        int i=0;
        while(i<n)
        {
            int start=i;   //记录起始位置
            while(i<n&&s[i]==s[start]) i++;   //找出该字符相同子字符串的长度
            if(i-start==k) return true;   //判断相同字符的长度时候为K
        }
        return false;
    }
};

1957. 删除字符使字符串变好

题解:连续字符的个数不超过3,可以在内循环中判断以下该重复字符是第几个,能否进行插入。

class Solution {
public:
    string makeFancyString(string s) {
        int n=s.size();
        int i=0;
        string ret;
        while(i<n)
        {
            int start=i;   //记录起始位置
            while(i<n&&s[i]==s[start]) 
            {
                if(i-start<2) ret+=s[i];  //连续字符个数小于3就可以插入结果
                i++;
            }
        }
        return ret;
    }
};

674. 最长连续递增序列

题解:找出最长的子数组,遍历数组;通过外层循环记录起始位置,内层循环找出子数组结束的位置。

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        //找递增的子数组
        int n=nums.size();
        int i=0,ret=0;
        while(i<n)
        {
            int start=i; //记录起始位置
            while(i<n&&(i==start||nums[i]>nums[i-1])) i++;  //判断是否是递增子数组

            ret=max(ret,i-start);  //更新答案
        }
        return ret;
    }
};

978. 最长湍流子数组

题解:湍流数组,递增和递减是交替进行的。外层循环进行记录起始位置和过滤相同位置,内层循环找出子数组的最长长度。细节:用flag记录i位置前面的增长趋势,使用flag*(nums[i+1]-nums[i])<0时注意乘法可能会导致越界。

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        //数组一增一减的趋势
        int n=arr.size();
        if(n==1) return 1;
        int i=0,ret=1;
        while(i<n-1)
        {
            if(arr[i]==arr[i+1]) //相等的数据直接过滤掉
            {
                i++;
                continue;
            }
            int start=i++;  //记录起始位置
            int flag=arr[i]-arr[start]>0?1:-1;  //记录上一个位置是递增还是递减
            while(i<n-1&&(flag*(arr[i+1]-arr[i])<0)) 
            {
                flag=arr[i+1]-arr[i]>0?1:-1;    //更新flag记录的位置
                i++;
            }

            ret=max(ret,i-start+1);  //更新答案
        }
        return ret;
    }
};

2110. 股票平滑下跌阶段的数目

题解:外层循环记录起始位置,内层循环找从该位置开始最长的平缓数组长度K,根据子数组的长度再求出其子数组的个数:K(K+1)/2;

class Solution {
public:
    long long getDescentPeriods(vector<int>& prices) {
        //一段平滑下跌子数组的长度为k
        //其满足条件的子数组个数为:k+k-1+k-2+k-3....+1为(k+1)*k/2

        int n=prices.size();
        long long i=0,ret=0;
        while(i<n)
        {
            int start=i; //记录数组起始位置
            while(i<n-1&&prices[i]-prices[i+1]==1) i++;  //找出最长平缓数组长度

            long long k=i-start+1;  //数组长度
            ret+=(k+1)*k/2;   //子数组个数
            i++;
        }
        return ret;
    }
};

228. 汇总区间

题解:与上一题类似,找连续递增的子数组,并且每次递增1。

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        //进行分组循环
        int n=nums.size();
        int i=0;
        vector<string> ret;
        while(i<n)
        {
            int start=i;  //记录起始位置
            while(i<n-1&&(long long)nums[i+1]-nums[i]==1) i++;  //找有多少连续的数

            if(i==start)   //判断是区间还是一个数
            ret.push_back(to_string(nums[start]));
            else 
            {
                string tmp=to_string(nums[start]);
                tmp+="->";
                tmp+=to_string(nums[i]);
                ret.push_back(tmp);
            }
            i++;
        }
        return ret;
    }
};

1887. 使数组元素相等的减少操作次数

题解:进行模拟;先对数组进行排序,从后往前遍历,当前位置与前一个位置不同时要对[i,n-1]进行改变;向前遍历直到遍历到0停止。

class Solution {
public:
    int reductionOperations(vector<int>& nums) {
        //先对数组进行排序
        int n=nums.size();
        sort(nums.begin(),nums.end());
        //从n-1开始往前找
        int i=n-1,ret=0;
        while(i>0)
        {
            if(nums[i-1]!=nums[i]) ret+=n-i;
            i--;
        }
        return ret;
    }
};

845. 数组中的最长山脉

题解:使用分组循环,外循环记录起始位置,内循环用两个来分别记录左侧山脉长度和右侧山脉长度。

class Solution {
public:
    int longestMountain(vector<int>& arr) {
        //分别求单调递增和递减区间
        int ret=0,n=arr.size();
        int i=0;
        while(i<n-1)
        {
            int start=i,flag1=0,flag2=0;  //用flag来记录是否存在左右山脉
            while(i<n-1&&arr[i]<arr[i+1])  //记录山脉左侧
            {
                flag1=1;
                i++;
            }
            while(i<n-1&&arr[i]>arr[i+1])  //记录山脉右侧
            {
                flag2=1;
                i++;
            }
            if(flag1&&flag2)
            ret=max(ret,i-start+1);
            if(i<n-1&&arr[i]==arr[i+1]) i++;  //对于后面数据相等时,向后移动防止死循环
        }
        return ret;
    }
};

2038. 如果相邻两个颜色均相同则删除当前颜色

题解:通过相同子字符串的长度就可以判断出一个玩家对于该子字符串可以进行多少次删除操作,比如AAAAA,Alice可以进行3次删除操作;因此通过分组循环分别记录两个玩家可以进行删除操作的次数就可以确定玩家。

class Solution {
public:
    bool winnerOfGame(string colors) {
        //通过记录重复字符的个数来判断每个人能够进行多少次删除操作
        int dela=0,delb=0;
        int n=colors.size();
        int i=0;
        while(i<n)
        {
            int start=i;  //存储起始位置
            while(i<n&&colors[i]==colors[start]) i++;  //判断相同字符的长度

            if(i-start>=3&&colors[start]=='A') dela+=i-start-2;   //判断A和B可以删除的次数
            else if(i-start>=3&&colors[start]=='B') delb+=i-start-2; 
        }
        return dela>delb;
    }
};

2900. 最长相邻不相等子序列 I

题解:注意题目中:“不能出现连续的0或1”,不能出现连续的0或1,则对于连续的0或1,我们只取其中一个添加到答案中即可;使用分组循环找出连续的0或1。

class Solution {
public:
    vector<string> getLongestSubsequence(vector<string>& words, vector<int>& groups) {
        int n=words.size();
        int i=0;
        vector<string> ret;  //记录结果
        while(i<n)
        {
            int start=i;  //记录起始位置
            while(i<n&&groups[i]==groups[start]) i++; 
            ret.push_back(words[start]);
        }   
        return ret;
    }
};

3011. 判断一个数组是否可以变为有序

题解:使用分组循环将数组依据二进制中1的个数进行分配,将每一组的元素大小与上一组中最大值进行比较,判断是否可以进行排序。关于二进制中1的计算可以使用内置函数

__builtin_popcount(int n)
class Solution {
    //计算二进制位中1的个数
    int setbit(int n)
    {
        int count = 0;
        while (n)
        {
            n = n & (n - 1);
            count++;
        }
        return count;
    }

public:
    bool canSortArray(vector<int>&  nums) {
        int pre_max = 0;  //记录上一个最小值
        int n = nums.size(), i = 0;
        while (i < n )
        {
            int start = setbit(nums[i]);  //记录开始位置的二进制位个数
            int in_max = 0;
            while (i < n && setbit(nums[i]) == start)
            {
                if(nums[i]<pre_max) return false;  //判断当前位置元素大小是否比上一组最大元素大
                in_max = max(in_max, nums[i]);  //记录该组元素最大值
                i++;
            }
            pre_max = in_max;
        }
        return true;
    }
};

1578. 使绳子变成彩色的最短时间

题解:分组循环,将相同颜色的气球作为一组,将相同颜色的气球进行删除,删除时为了花费最小的时间,将花费时间长的进行保留。

class Solution {
public:
    int minCost(string colors, vector<int>& neededTime) {
        //将有多余颜色的气球删除,保留连续颜色中移除时间最小的一个
        int n=colors.size();
        int i=0,ret=0;
        while(i<n)
        {
            int start=i;  //记录起始位置
            long sum=0,in_max=0;   //记录所有气球的总花费时间,以及最长花费时间
            while(i<n&&colors[i]==colors[start]) 
            {
                sum+=neededTime[i];
                in_max=max(in_max,(long)neededTime[i]);
                i++;
            }

            if(i-start>1) ret+=sum-in_max;
        }
        return ret;
    }
};

1759. 统计同质子字符串的数目

题解:计算每一组相同字符串的长度K,每一组会使得结果增加k+k-1+k-2+k-3...+2+1 即(k+1)*k/2。

class Solution {
    #define MOD 1000000007 
public:
    int countHomogenous(string s) {
        //计算每一组相同字符串的长度K,每一组会使得结果增加k+k-1+k-2+k-3...+2+1 即(k+1)*k/2
        
        int n=s.size();
        int i=0;
        long long ret=0;
        while(i<n)
        {
            int start=i;
            while(i<n&&s[i]==s[start]) i++;

            long long len=i-start;
            ret+=(len+1)*len/2;
        }
        return ret%MOD;
    }
};

1839. 所有元音按顺序排布的最长子字符串

题解:分组循环,以每一个a开头的字符串作为一组,记录该字符串长度并判断是否由所有元音字母按顺序组成的。

class Solution {
public:
    int longestBeautifulSubstring(string word) {
        vector<char> vowel({'a','e','i','o','u'});

        int n=word.size();
        int i=0,ret=0;
        while(i<n)
        {
            while(i<n&&word[i]!='a') i++;  //找以a开头的字符串

            int start=i,pos=0;
            for(pos=0;pos<5;pos++)  //遍历所有元音字符
            {
                if(word[i]!=vowel[pos]) break;  //判断是否存在该元音字符
                while(word[i]==vowel[pos]) i++;
            }        
            if(pos==5) ret=max(ret,i-start);  //更新答案
        }
        return ret;
    }
};

2765. 最长交替子数组

题解:根据s[m - 1] - s[m - 2] = (-1)^m可以推出nums[ i - 1]==nums[ i + 1];一次对数组进行分组以s1=s0+1的位置为起始位置,进行循环找到最长子数组。

class Solution {
public:
    int alternatingSubarray(vector<int>& nums) {
        int i=0,n=nums.size();
        int ret=-1;
        while(i<n)
        {
            while(i<n-1&&nums[i+1]-nums[i]!=1) i++;  //找到第一个满足条件的位置
            if(i==n-1) return ret;

            int start=i++;
            while(i<n-1&&nums[i-1]==nums[i+1]) i++;  //根据s[m - 1] - s[m - 2] = (-1)^m 
                                                    //可以推出nums[i-1]==nums[i+1]
            ret=max(ret,i-start+1);
        }
        return ret;
    }
};

3255. 长度为 K 的子数组的能量值 II

题解:分组循环,将每一个递增为1的子数组作为一组,当该子数组的长度>=k的时候就有能量值,继续向后查找看子数组是否还能更长,如果能则nums[i]就是下一组的能量值。对于输出数组可以与原数组之间建立位置关系,i----->i-k+1的位置,这样就可以依据i位置确定答案更新到的具体下标位置。

class Solution {
public:
    vector<int> resultsArray(vector<int>& nums, int k) {
        //使用分组循环,将连续递增的数据分为一组,当该组的长度>=k的时候就可以将最大元素放入即nums[i]
        //根据放入元素下标可以与返回结果下标建立联系,nums[i]放在i-k+1的位置
        
        if(k==1) return nums;  //对于k==1就不需要找子数组,直接进行返回即可
        int n=nums.size();
        vector<int> ret(n-k+1,-1);   //将数组初始化为-1
        int i=0;
        while(i<n)
        {
            int start=i;    //记录起始位置
            i++;
            while(i<n&&nums[i-1]+1==nums[i]) 
            {
                //子数组长度为i-start+1;
                if(i-start+1>=k) ret[i-k+1]=nums[i];  //当子数组长度大于等于k时就可以进行插入
                i++;
            }
        }
        return ret;
    }
};

3350. 检测相邻递增子数组 II

题解:根据题意,找两个长度相同相邻且都是严格递增的数组;使用分组循环,计算出一个长度为len的连续递增子数组后,1)可以与上一个相邻子数组进行组合,找到合适的k,k就是两个连续数组长度的较小的哪一个;2)还可以将长度为len的数组拆分为两个长度为len/2的数组,也是满足条件的。

class Solution {
public:
    int maxIncreasingSubarrays(vector<int>& nums) {
        //分组循环,对每次查找的递增数组进行记录
        int n=nums.size();
        int i=0;
        int prev=0,k=0;
        while(i<n)
        {
            int start=i++;
            while(i<n&&nums[i-1]<nums[i]) i++;

            int len=i-start;
            k=max(k,len/2);   //对该数组进行拆分
            k=max(k,min(len,prev));   //当前数组长度与上一个数组长度对比

            prev=len;
        }
        return k;
    }
};

3105. 最长的严格递增或递减子数组

题解:找严格递增和严格递减的数组,使用分组循环,将每一个严格递增或递减的数组分为一组。

class Solution {
public:
    int longestMonotonicSubarray(vector<int>& nums) {
        //简单的分组循环
        int n=nums.size();
        int i=0,ret=1;
        while(i<n-1)
        {
            int start=i++;
            int flag=nums[i]-nums[start];  //记录是递增还是递减的
            while(flag<0&&i<n-1&&nums[i]>nums[i+1]) i++;
            while(flag>0&&i<n-1&&nums[i]<nums[i+1]) i++;

            if(flag!=0) ret=max(ret,i-start+1);   //相等不需要进行更新
        }
        return ret;
    }
};

拓展 (加餐)

838. 推多米诺

题解:可能会出现的情况有4种:1)[L,L],[R,R]这两种都是可以直接对数组进行修改的;2)[L,R]是不需要进行操作的;3)[R,L]是需要对中间进行操作的。

如果数组最前面一个非.字符是L的话,为了组成[L,L]的形式,在原数组前面插上一个哨兵为L,结尾一样插入一个R

class Solution {
public:
    string pushDominoes(string dominoes) {
        //将区间可以分为始终[L,L],[R,R]这两种都是可以直接对数组进行修改的
        //[L,R]是不需要进行操作的;
        //[R,L]是需要对中间进行操作的

        // 如果数组最前面一个非.字符是L的话,为了组成[L,L]的形式,在原数组前面插上一个哨兵为L,结尾一样插入一个R
        string s="L"+dominoes+"R";
        int n=s.size();
        int prev=0;  //记录上一个字符位置
        for(int i=1;i<n;i++)
        {
            if(s[i]=='.') continue;
            if(s[prev]==s[i])
            {
                //是[L,L]或者[R,R]
                //从[prev,i]都是相同的L或者R
                fill(s.begin() + prev + 1, s.begin() + i, s[prev]);
            }

            else if(s[i]=='L')
            {
                //[R,L]的情况
                //一共有len=i-prev+1个元素
                int len=i-prev+1;
                //将其分为两半(prev,prev+len/2] [i-len/2+1,i)
                fill(s.begin() + prev + 1, s.begin() + prev + len / 2, s[prev]);
                fill(s.begin() + i - len / 2 + 1, s.begin() + i, s[i]);
            }
            prev = i;
        }
        return s.substr(1,n-2);
    }
};

467. 环绕字符串中唯一的子字符串

题解:因为不能有重复的子字符串所以要进行筛选,如果直接存储每个已经存在的子字符串效率太低;此处可以使用一个哈希表来存储以每一个字符为结尾的子字符串的个数;

以当前字符为结尾,使得循环可以一直向后走也能保证记录每一个子字符串。

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        //环绕字符串种唯一的子字符串
        //因为不能有重复的子字符串所以要进行筛选
        //如果直接存储每个已经存在的子字符串效率太低
        
        //可以使用一个哈希表来存储每一个以当前字符结尾的子字符串的个数
        //以当前字符为结尾,使得循环可以一直向后走也能记录每一个子字符串

        int n=s.size();
        int i=0,ret=0;
        int l=0;
        vector<int> length(26,0);  //记录以每一个字符为结尾字符串的长度
        for(int i=0;i<n;i++)
        {
            if(i>0&&(s[i-1]-'a'+1)%26!=s[i]-'a') l=i;  //如果不是环绕字符串重新开始

            int len=i-l+1;    //计算前面环绕字符串的长度
            if(len>length[s[i]-'a'])   //如果以当前字符结尾的字符串长度比之前数组种记录的更长就需要对结果进行更新
            {
                ret+=len-length[s[i]-'a'];
                length[s[i]-'a']=len;
            }
        }
        return ret;
    }
};

3499. 操作后最大活跃区段数 I

题解:根据题意就是找  0----1----0区段中1旁边两个0个数之和最大值,将这些0变成1后活跃区段就是最大值。可以先将所有的0区段的长度进行存储,再找出相邻区段和的最大值。

class Solution {
public:
    int maxActiveSectionsAfterTrade(string s) {
        //找0  ---- 1 ---- 0的区间,将每一个0区间段的个数进行记录,然后找到两个相邻区段最大和
        int n=s.size();
        int i=0,ret=0;
        vector<int> zero;   //记录每一个区间中0的个数
        while(i<n)
        {
            while(i<n&&s[i]=='1') i++,ret++;

            int start=i;
            while(i<n&&s[i]=='0') i++;
            if(i!=start) zero.push_back(i-start);   
        }
        
        int r=0,l=0,more=0,tmp=0;  //查找相邻两个区间最大和
        while(r<zero.size())
        {
            while(r<zero.size()&&r-l<2) 
                tmp+=zero[r++];

            if(r-l==2&&tmp>more) more=tmp;
            tmp-=zero[l++];
        }
        return ret+more;
    }
};

可以对上面代码进行优化,不进行0的个数存储,只记录两个0区段之和的最大值。

class Solution {
public:
    int maxActiveSectionsAfterTrade(string s) {
        //进行优化
        int n=s.size(),i=0;
        int ret=0;
        int prev=INT_MIN,zero=0;   //用prev记录上一个0区段的长度,zero记录相邻两个0区段和的最大值
        while(i<n)
        {
            int start=i;
            while(i<n-1&&s[i]==s[i+1]) i++;  

            int len=i-start+1;  //计算该区段的长度
            if(s[start]=='1') ret+=len;   //如果是1直接加入答案
            else
            {
                zero=max(zero,prev+len);     //与最大值进行比较
                prev=len;
            }
            i++;
        }
        return ret+zero;
    }
};

2593. 标记所有元素后数组的分数

题解:对数组下标和元素大小进行排序,从小到大依次进行选择,将被选择的元素左右下标元素进行删除即可。

class Solution {
public:
    long long findScore(vector<int>& nums) {
        //带下标进行排序
        int n=nums.size();
        vector<pair<int,int>> tmp;
        for(int i=0;i<n;i++)
        tmp.push_back({nums[i],i});

        sort(tmp.begin(),tmp.end(),[](pair<int,int> x,pair<int,int> y){
            if(x.first!=y.first) return x.first<y.first;
            else return x.second<y.second;
            });   //根据元素大小和下标进行排序

        //进行选择
        int i=0;
        long long ret=0;
        unordered_set <int> del;  //存储被删除元素的下标
        for(int i=0;i<n;i++)
        {
            int index=tmp[i].second;
            if(del.count(index)==0)
            {
                ret+=tmp[i].first;
                del.insert(index+1);
                del.insert(index-1);
            }
        }
        return ret;
    }
};

2948. 交换得到字典序最小的数组

题解:如果一组数字之间总有两个数之差<=limit则这组数可以进行任意交换,也就意味着可以将其交换为有序的;所以此题就转化为了找一组数组该组数字间中有两数之差<=limit,如果直接对整个数组进行查找效率太低,此次可以向进行排序,排序后就很容易查找了,但是排序之前还要将数组元素大小与数组下标绑定,否则在后面进行交换时找不到具体下标位置。

在排序后,通过分组循环找到子数组其相邻元素之差<=limit,这样的子数组就可以进行任意交换,再根据保留的下标将下标和元素大小进行对应。

class Solution {
public:
    vector<int> lexicographicallySmallestArray(vector<int>& nums, int limit) {
        //可以先将数组进行排序,根据limit判断那一组的数字是可以进行交换的
        //但是在排序后数组就是乱序的,所以排序时要带上下标
        int n=nums.size();
        vector<pair<int,int>> tmp;
        for(int i=0;i<n;i++)
            tmp.push_back({nums[i],i});

        //对数组进行排序
        sort(tmp.begin(),tmp.end(),[](pair<int,int> x,pair<int,int> y){ return x.first<y.first;});  //只需要根据元素大小进行排序即可
        
        //使用分组循环,根据limit判断那些数字是可以进行交换的
        vector<int> ret(n);
        int i=0;
        while(i<n)
        {
            int start=i++;
            while(i<n&&tmp[i].first<=tmp[i-1].first+limit) i++;   

            //提取出[start,i)位置的下标
            vector<int> index;
            for(int j=start;j<i;j++) index.push_back(tmp[j].second);

            //对数组下标进行排序
            sort(index.begin(),index.end());
            
            //将下标和元素大小的对应关系映射到答案上
            for(int k=0;k<index.size();k++)
            ret[index[k]]=tmp[k+start].first;
        }
        return ret;
    }
};

总结

分组循环有点类似与同向双指针,在处理分组循环类题目时关键点在于正确设计内外层循环的分工,并处理好边界条件。该模板可解决大多数需要统计连续子数组特征的竞赛题目。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2401187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

焊缝缺陷焊接缺陷识别分割数据集labelme格式5543张4类别

数据集中有超过一半为增强图片&#xff0c;请认真观察图片预览 数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;5543 标注数量(json文件个数)&#xff1a;5543 标注类别数&#xff1a;4…

关于scrapy在pycharm中run可以运行,但是debug不行的问题

关于scrapy在pycharm中run模式可以运行&#xff0c;但是debug模式不行的问题 文章目录 关于scrapy在pycharm中run模式可以运行&#xff0c;但是debug模式不行的问题查了下原因 点击run就可以运行&#xff0c;但是debug就是运行不了 一点击debug就报这个错&#xff0c;也不知道啥…

Java高级 | 【实验四】Springboot 获取前端数据与返回Json数据

隶属文章&#xff1a; Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a; Java高级 | 【实验一】Spring Boot安装及测试 最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot …

Prj08--8088单板机C语言8255读取按键码

1.验证结果 2.代码片 key_codeinp(PORT_8255_C)&0x0f;tiny_sprintf(buffer,"Key_code 0X%x \r\n",key_code);uart_str_send(buffer); 3.完整代码 #include "tiny_stdarg.h" // 使用自定义可变参数实现#define ADR_273 0x0200 #define ADR_244 0x…

蜜獾算法(HBA,Honey Badger Algorithm)

2021年由Hashim等人提出&#xff08;论文&#xff1a;Honey Badger Algorithm: A New Metaheuristic Algorithm for Solving Optimization Problems&#xff09;。模拟蜜獾在自然界中的智能捕食行为&#xff0c;属于群体智能优化算法&#xff08;与粒子群PSO、遗传算法GA同属一…

Modbus转Ethernet IP网关助力罗克韦尔PLC数据交互

在工业自动化领域&#xff0c;Modbus协议是一种广泛应用的串行通信协议&#xff0c;它定义了主站和从站之间的通信规则和数据格式。罗克韦尔PLC是一种可编程的逻辑控制器&#xff0c;通过Modbus协议实现与其他设备之间的数据交互。然而&#xff0c;随着以太网技术的普及和发展&…

飞算JavaAI 炫技赛重磅回归!用智能编码攻克老项目重构难题

深夜还在排查十年前Hibernate框架埋下的N1查询隐患&#xff1f;跨语言迁移时发现SpringMVC控制器里的业务逻辑像一团乱麻&#xff1f;当企业数字化进入深水区&#xff0c;百万行代码的老系统就像一座随时可能崩塌的"技术债冰山"。近日&#xff0c;飞算科技发布JavaAI…

ToolsSet之:XML工具

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Text菜单下的XML Tool工具是一个Xml工…

keepalived定制日志bug

keepalived定制日志bug 源码安装apt安装endl 源码安装 在/etc/rsyslog.d/目录下创建 keepalived的日志配置文件keepalived.conf [rootubuntu24-13:~]# vim /etc/rsyslog.d/keepalived.conf [rootubuntu24-13:~]# cat /etc/rsyslog.d/keepalived.conf local6.* /var/log/keepa…

数据库系统概论(十三)详细讲解SQL中数据更新(插入,修改与更新)

数据库系统概论&#xff08;十三&#xff09;详细讲解SQL中数据更新 前言一、数据插入1. 插入数据是什么&#xff1f;2.插入单条数据&#xff08;插入元组&#xff09;场景 1&#xff1a;指定部分列插入场景 2&#xff1a;不指定列名&#xff08;插入所有列&#xff09;场景 3&…

极客时间-《搞定音频技术》-学习笔记

极客时间-《搞定音频技术》-学习笔记 语音基础知识 https://www.zhangzhenhu.com/audio/feature.html 序章-0 作者说这个语音技术啊&#xff0c;未来肯定前景大好啊&#xff0c;大家都来学习&#xff0c;然后给出了课程的脑图 音频基础 什么是声音 声音的三要素是指响度、…

网络攻防技术十三:网络防火墙

文章目录 一、网络防火墙概述1、网络型防火墙&#xff08;网络防火墙&#xff09;2、Web应用防火墙3、数据库防火墙4、主机防火墙&#xff08;个人防火墙&#xff09;5、网络防火墙的功能 二、防火墙工作原理1、无状态包过滤防火墙2、有状态包过滤防火墙&#xff08;状态检测/动…

Express 集成Sequelize+Sqlite3 默认开启WAL 进程间通信 Conf 打包成可执行 exe 文件

代码&#xff1a;express-exe: 将Express开发的js打包成exe服务丢给客户端使用 实现目标 Express 集成 Sequelize 操作 Sqlite3 数据库&#xff1b; 启动 Sqlite3 时默认开启 WAL 模式&#xff0c;避免读写互锁&#xff0c;支持并发读&#xff1b; 利用 Conf 实现主进程与 Ex…

2024年认证杯SPSSPRO杯数学建模D题(第二阶段)AI绘画带来的挑战解题全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 D题 AI绘画带来的挑战 原题再现&#xff1a; 2023 年开年&#xff0c;ChatGPT 作为一款聊天型AI工具&#xff0c;成为了超越疫情的热门词条&#xff1b;而在AI的另一个分支——绘图领域&#xff0c;一款名为Midjourney&#xff08;MJ&#xff…

DOCKER使用记录

1、拉取镜像 直接使用docker pull <image>&#xff0c;大概率会出现下面的报错信息&#xff1a; (base) jetsonyahboom:~$ docker pull ubuntu:18.04 Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while …

【深度学习相关安装及配环境】Anaconda搭建虚拟环境并安装CUDA、cuDVV和对应版本的Pytorch,并在jupyter notebook上部署

目录 1. 查看自己电脑的cuda版本2.安装cuda关于环境变量的配置测试一下&#xff0c;安装完成 3.安装cuDVV环境变量的配置测试一下&#xff0c;安装完成 4.创建虚拟环境先安装镜像源下载3.11版本py 5.在虚拟环境下&#xff0c;下载pytorch6.验证是否安装成功7.在jupyter noteboo…

web3-区块链基础:从区块添加机制到哈希加密与默克尔树结构

区块链基础&#xff1a;从区块添加机制到哈希加密与默克尔树结构 什么是区块链 抽象的回答: 区块链提供了一种让多个参与方在没有一个唯一可信方的情况下达成合作 若有可信第三方 > 不需要区块链 [金融系统中常常没有可信的参与方] 像股票市场&#xff0c;或者一个国家的…

TCP小结

1. 核心特性 面向连接&#xff1a;通过三次握手建立连接&#xff0c;四次挥手终止连接&#xff0c;确保通信双方状态同步。 TCP连接建立的3次握手 抓包&#xff1a; client发出连接请求&#xff1b; server回应client请求&#xff0c;并且同步发送syn连接&#xff1b; clien…

Python 打包指南:setup.py 与 pyproject.toml 的全面对比与实战

在 Python 开发中&#xff0c;创建可安装的包是分享代码的重要方式。本文将深入解析两种主流打包方法——setup.py 和 pyproject.toml&#xff0c;并通过一个实际项目示例&#xff0c;展示如何使用现代的 pyproject.toml 方法构建、测试和发布 Python 包。 一、setup.py 与 pyp…

性能优化 - 案例篇:缓存_Guava#LoadingCache设计

文章目录 Pre引言1. 缓存基本概念2. Guava 的 LoadingCache2.1 引入依赖与初始化2.2 手动 put 与自动加载&#xff08;CacheLoader&#xff09;2.2.1 示例代码 2.3 缓存移除与监听&#xff08;invalidate removalListener&#xff09; 3. 缓存回收策略3.1 基于容量的回收&…