两种解法:1、按条件暴力搜索+剪枝 2、滑动窗口+双端队列维护最值的索引

1、两层循环,嘎嘎暴力,剪枝思路: (1)nums[i]范围不满足,直接开启下一轮循环; (2)从索引i开始的剩余元素数量,比当前最长稳定序列小,和整个循环说“告辞”; (3) 如果稳定字符串因为下一个值范围越界而结束,[i + 1, end]一定比原来[i, end]小,所以i直接从 end后面开启循环。 如果稳定字符串因为区间最值之差大于4而结束,则i要从i+1就重新进入循环,如:[18, 20, 21, 22, 24]两个最长稳定子序列[0,3]和[1,4].

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int N;
    cin >> N;
    vector<int> nums(N);
    for (int i = 0; i < N; ++i) {
        cin >> nums[i];
    }
    int maxSteadyLength = 1;
    vector<pair<int, int>> res;
    for (int i = 0; i < N;) {
        if (nums[i] < 18 || nums[i] > 24) {
            ++i;
            continue;
        }
        if (N - i < maxSteadyLength) break;
        int start = i, end = i;
        int tempMin = nums[i], tempMax = nums[i];
        int tempLength = 1;
        // tempMax - tempMin <= 4 判断下一轮增加长度后的窗口是否满足稳定性,提前终止无意义遍历
        for (int j = i; tempMax - tempMin <= 4 && nums[j + 1] <= 24 &&
                nums[j + 1] >= 18 && j < N - 1; ++j) {
            tempMax = max(tempMax, nums[j + 1]);
            tempMin = min(tempMin, nums[j + 1]);
            // 判断新增元素后是否能构成有效稳定子数组,只有校验通过才更新子数组最大长度
            if (tempMax - tempMin <= 4) {
                tempLength++;
                maxSteadyLength = max(maxSteadyLength, tempLength);
                end = j + 1;
            } else {
                end = j;
            }
        }
        res.emplace_back(start, end);
        i = (nums[end + 1] <= 24 && nums[end + 1] >= 18) ? i + 1 : end + 2;
    }
    for(auto [s, e] : res){
        if (maxSteadyLength == e - s + 1) {
            cout << s << " " << e << endl;
        }
    }
    return 0;
}

2、滑动窗口 (1)对比上面思路,相当于:每轮循环j不用从i开始重新记录满足条件最值(j不走回头路,用双端队列记录最值情况)i右移缩小窗口,第一次满足max-min <=4时,[i, j]长度一定最大。 (2)最大/小值索引队列(为什么不存普通最值?):可以天然存储多个相同最值,当left右移时方便确定nums[left]是否对最值队列产生影响,如果有则移除该影响。

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

int main() {
    int N;
    cin >> N;
    vector<int> nums(N);
    for (int i = 0; i < N; ++i) {
        cin >> nums[i];
    }
    int maxSteadyLength = 1;
    vector> res;
    deque<int> max_queue; // 队首存储窗口最大值索引
    deque<int> min_queue; // 队首存储窗口最小值索引
    int left = 0;
    for(int right = 0; right < N; ++right){
        // 对于当前nums[right]与队尾元素比较,维护最大/小值索引队列
        while(!max_queue.empty() && nums[max_queue.back()] < nums[right]){
            max_queue.pop_back();
        }
        max_queue.push_back(right);
        while(!min_queue.empty() && nums[min_queue.back() ]> nums[right]){
            min_queue.pop_back();
        }
        min_queue.push_back(right);
        // 如果nums[right]不在稳定范围内[18, 24],清空最大/小值索引队列
        if(nums[right] < 18 || nums[right] > 24){
            max_queue.clear();
            min_queue.clear();
            left = right + 1;
            continue;
        }
        // 向右收缩left,确保窗口内的最值差<=4,并清空nums[left]对最大/小值索引队列的影响
        while(!max_queue.empty() && !min_queue.empty()){
            int curr_max = nums[max_queue.front()];
            int curr_min = nums[min_queue.front()];
            if(curr_max - curr_min > 4){
                // 这里要做索引判断!!!
                if(max_queue.front() == left){ 
                    max_queue.pop_front();
                }
                if(min_queue.front() == left){
                    min_queue.pop_front();
                }
                left++;
            }else {
                break;
            }
        }
        // 计算当前窗口大小,符合条件结果加入结果集
        int curr_length = right -  left + 1;
        if(curr_length > maxSteadyLength){
            maxSteadyLength = curr_length;
            res.clear();
            res.emplace_back(left, right);
        }else if (curr_length == maxSteadyLength) {
            res.emplace_back(left, right);
        }
    }
    for(auto [s, e] : res){
        if (maxSteadyLength == e - s + 1) {
            cout << s << " " << e << endl;
        }
    }
    return 0;
}

链接