[Algorithm][滑动窗口][无重复字符的最长字串][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

发布于:2024-04-19 ⋅ 阅读:(19) ⋅ 点赞:(0)


1.无重复字符的最长字串

1.题目链接


2.算法原理详解

  • 研究的对象依旧是⼀段连续的区间,因此继续使⽤「滑动窗⼝」思想来优化
    • 滑动窗口 + 哈希表(判断字符是否重复出现)
  • 让滑动窗⼝满⾜:窗⼝内所有元素都是不重复的
  • 做法:右端元素s[right]进⼊窗⼝的时候,哈希表统计这个字符的频次
    • 如果这个字符出现的频次超过1 ,说明窗⼝内有重复元素
      • 那么就从左侧开始划出窗⼝, 直到s[right]这个元素的频次变为1,然后再更新结果
    • 如果没有超过1,说明当前窗⼝没有重复元素,可以直接更新结果
      请添加图片描述

3.代码实现

int LengthOfLongestSubstring(string s) 
{
    int n = s.size(); 
    int ret = 0;
    int hash[128] = { 0 }; // 利用hash查重

    for(int left = 0, right = 0; right < n; right++)
    {
        hash[s[right]]++; // 入窗口

        while(hash[s[right]] > 1)
        {
            hash[s[left++]]--; // 出窗口
        }

        ret = max(ret, right - left + 1); // 更新结果
    }

    return ret;
}

2.最大连续的一个数 Ⅲ

1.题目链接


2.算法原理详解

  • 不要去想怎么翻转,不要把问题想的很复杂
    • 这道题的结果⽆⾮就是⼀段连续的1中间塞了k个0
  • 问题转化为:子数组内,0的个数不超过k
    • 既然是连续区间,可以考虑使⽤**「滑动窗⼝」**来解决问题
  • 做法:当right < nums.size()时,⼀直下列循环:
    • 让当前元素进⼊窗⼝
      • 1 -> 无视
      • 0 -> zero++
    • 检查0的个数是否超标
      • 如果超标,依次让左侧元素滑出窗⼝
      • 顺便更新zero的值,直到0的个数恢复正常
    • 程序到这⾥,说明窗⼝内元素是符合要求的,更新结果
    • right++,处理下⼀个元素
      请添加图片描述

3.代码实现

int LongestOnes(vector<int>& nums, int k) 
{
    // 问题转化为,子数组内,0的个数不超过k
    int ret = 0;
    for(int left = 0, right = 0, zero = 0; right < nums.size(); right++)
    {
        if(nums[right] == 0)
        {
            zero++; // 入窗口
        }

        while(zero > k)
        {
            if(nums[left++] == 0)
            {
                zero--;
            }
        }

        ret = max(ret, right - left + 1);
    }

    return ret;
}

3.将x减到0的最小操作数

1.题目链接


2.算法原理详解

  • 要求是数组「左端+右端」两段连续的、和为x的最短数组
    • 可以转化成求数组内⼀段连续的、和为sum(nums) - x的最⻓数组
      • 即:将两端转化为中间连续
    • 此时,就是熟悉的**「滑动窗⼝」**问题了
  • 做法
    • 转化问题:求target = sum(nums) - x
      • 如果target < 0,问题⽆解
    • 初始化左右指针left = 0, right = 0(滑动窗⼝区间表⽰为 [ l , r ) [l, r) [l,r)
      • 左右区间是否开闭很重要,必须设定与代码⼀致
      • 记录当前滑动窗⼝内数组和的变量sum = 0
      • 记录当前满⾜条件数组的最⼤区间⻓度len = -1
    • right < nums.size()时,⼀直循环:
      • if sum < target
        • right++,直⾄sum >= target ,或right已经移到头
      • if sum > target
        • left++,直⾄sum <= target ,或left已经移到头
      • 如果经过前两步的左右移动使得sum == target
        • 维护满⾜条件数组的最⼤⻓度,并让下个元素进⼊窗⼝
          请添加图片描述

3.代码实现

int MinOperations(vector<int>& nums, int x) 
{
    // 将模型转化为最长子数组的和 == (sumNum - x)
    int sum = 0, ret = -1;
    int target = -x;

    for(auto& e : nums)
    {
        target += e;
    }

    // 细节处理,当target为负数时,怎么减都减不够
    if(target < 0)
    {
        return -1;
    }

    for(int left = 0, right = 0; right < nums.size(); right++)
    {
        sum += nums[right]; // 入窗口

        while(sum > target) // 判断
        {
            sum -= nums[left++]; // 出窗口
        }

        if(sum == target)
        {
            ret = max(ret, right - left + 1); // 更新结果
        }
    }

    return ret == -1 ? -1 : nums.size() - ret;
}