当前位置: 首页 > news >正文

优选算法 100 题 —— 2 滑动窗口

前一篇的链接:优选算法100 题 ——1 双指针

2.滑动窗口

2.1 长度最小的子数组

在这里插入图片描述

题目链接: 209. 长度最小的子数组

解法一:暴力枚举

两个指针定一移一,遍历完数组。

在暴力解法的基础上进行优化,可以得出解法二:滑动窗口算法。

解法二:利用单调性,使用“同向双指针”

在这里插入图片描述

关于 滑动窗口 五个点

  • 是什么? ——同向双指针

  • 在哪用?——暴力解法中存在单调性时。

  • 怎么用?—— 1. left = 0,right = 0 2. 进窗口 3. 判断 4. 出窗口 5. 更新结果。

  • 正确性——利用单调性,规避了多余枚举

  • 时间复杂度—— O(n)O(n)O(n)

class Solution {public int minSubArrayLen(int target, int[] nums) {int n = nums.length, len = Integer.MAX_VALUE, sum = 0;for (int left = 0, right = 0; right < n; right++) {sum += nums[right];while (sum >= target) {len = Math.min(len, right - left + 1);sum -= nums[left++];}}return len == Integer.MAX_VALUE ? 0 : len;}
}

2.2 无重复字符的最长子串

在这里插入图片描述

题目链接: 3. 无重复字符的最长子串

仍是在暴力枚举基础上进行改进

class Solution {public int lengthOfLongestSubstring(String s) {int[] hash = new int[128];int n = s.length(), len = 0, left = 0, right = 0;while (right < n) {hash[s.charAt(right)]++;while(hash[s.charAt(right)] > 1) {hash[s.charAt(left++)]--;}len = Math.max(len, right - left + 1);right++;}return len;}
}

这里用 用 int 数组充当 hash表 判断是否有重复元素。

2.3 最大连续1的个数 Ⅲ

在这里插入图片描述

题目链接: 1004. 最大连续1的个数 Ⅲ

class Solution {public int longestOnes(int[] nums, int k) {int left = 0, right = 0, len = 0, count = 0 , n = nums.length;while (right < n) {if (nums[right] == 0) {count++;}while (count > k) {if (nums[left++] == 0) count--;}len = Math.max(len, right - left + 1);right++;}return len;}
}

不翻转,只看 指针圈出的元素中零的个数。当 0 的个数超过 k 时,移动定指针,使 0 的个数变到合理范围内。

2.4 将 x 减到 0 的最小操作数

在这里插入图片描述

题目链接:1658. 将 x 减到 0 的最小操作数

思想:正难则反

观察题目,发现满足条件的数可能在最左边,或者最右边,甚至两边都有。从看到题的第一印象,正着找这些数的个数,很复杂。但是反过来,会发现那些不满足的数,总是连续的一组,找到他们的长度,用总长度减去,即得所求。

那么这道题就变成了,找出最长子数组的长度,是该数组所有元素之和等于 原数组总和 - x

class Solution {public int minOperations(int[] nums, int x) {int sum = 0;for (int num : nums) {sum += num;}int target = sum - x, len = -1, n = nums.length; if (target < 0 || target > sum) return -1;sum = 0;for (int left = 0, right = 0; right < n; right++) {sum += nums[right];while (sum > target) {sum -= nums[left++];}if (sum == target) {len = Math.max(len, right - left + 1);};}return len == -1 ? -1 : n - len;}
}

2.5 水果成篮

在这里插入图片描述

题目链接:904. 水果成篮

总结题目要求:找到只含有两个不同元素的最长子数组的长度。

解法一:暴力枚举 + 哈希 哈希用来,记录种类

解法二:滑动窗口 + 哈希

直接用 hash 表

class Solution {public int totalFruit(int[] fruits) {Map<Integer,Integer> map = new HashMap<>();int n = fruits.length, left = 0, right = 0, len = 0;while (right < n) {map.put(fruits[right], map.getOrDefault(fruits[right],0) + 1);while (map.size() > 2) {map.put(fruits[left],map.get(fruits[left]) - 1);if (map.get(fruits[left]) == 0) {map.remove(fruits[left]);}left++;}len = Math.max(len, right - left + 1);right++;}return len;}
}

数组模拟 hash

class Solution {public int totalFruit(int[] fruits) {int n = fruits.length, left = 0, right = 0, len = 0, kinds = 0;int[] hash = new int[n + 1];while (right < n) {if (hash[fruits[right]] == 0) kinds++;hash[fruits[right]]++;while (kinds > 2) {hash[fruits[left]]--;if (hash[fruits[left]] == 0) {kinds--;}left++;}len = Math.max(len, right - left + 1);right++;}return len;}
}

2.6 找到字符串中所有字母异位词

在这里插入图片描述

题目链接:438. 找到字符串中所有字母异位词

即在 s 中找到包含 p 中所有元素的区域(可以顺序不同)

通过使用 hash 表可以快速判断是否有 异位词。

解法:滑动窗口 + hash

class Solution {public List<Integer> findAnagrams(String s, String p) {int np = p.length(), ns = s.length();int[] hash = new int[26], hash2 = new int[26] ;List<Integer> ret = new ArrayList<>();for (int i = 0; i < np; i++) {hash[p.charAt(i) - 'a']++;}for (int left = 0, right = 0; right < ns; right++) {char in = s.charAt(right);hash2[s.charAt(right) - 'a']++;if (right - left + 1 > np) {hash2[s.charAt(left++) - 'a']--;}if (check(hash,hash2)) {ret.add(left);}}return ret;}public boolean check (int[] hash, int[] hash2) {for (int i = 0; i < 26; i++) {if (hash2[i] != hash[i]) {return false;}}return true;}
}

由于这里 “窗口” 在滑动中的大小是定长的,为 p.length()。所以不用 while 一直移动,用 if 即可。

优化:引入一个 count 变量

使用 count 变量,来记录窗口中 “有效字符的个数” 。有效,即 该元素是否在 p 中,以及在 hash2 中该元素的个数是否符合 p 中的个数。

举个例子:s: abaaccddjlkjlk,p: baad。 此时 left 指在 s 的 1 (从 0 开始),right 指在 4 。那么一共有 3 个有效字符。即 ” b a a“

只要 count == p.length() 时,即为异位了字符串 p

class Solution {public List<Integer> findAnagrams(String s, String p) {int np = p.length(), ns = s.length(), count = 0;int[] hash = new int[26], hash2 = new int[26] ;List<Integer> ret = new ArrayList<>();for (int i = 0; i < np; i++) {hash[p.charAt(i) - 'a']++;}for (int left = 0, right = 0; right < ns; right++) {int in = s.charAt(right) - 'a';if (hash2[in]++ < hash[in]) {count++;};if (right - left + 1 > np) {int out = s.charAt(left++) - 'a';if (hash2[out]-- <= hash[out]) {count--;}}if (count == np) {ret.add(left);}}return ret;}}

2.7 串联所有单词的子串

在这里插入图片描述

题目链接:30. 串联所有单词的子串

这道题同前一道题,原理相同,前一题是一个单词,这里是一个字符串。

在这里插入图片描述

如上图所示,为完全遍历。所以 left 要在不同的起始位置。从 0word[0].length()

class Solution {public List<Integer> findSubstring(String s, String[] words) {List<Integer> list = new ArrayList<>();int footlen = words[0].length();int len = words.length, slen = s.length();Map<String, Integer> map = new HashMap<>();for (String str : words) {map.put(str,map.getOrDefault(str, 0) + 1);}for (int i = 0; i < footlen; i++) {Map<String, Integer> map2 = new HashMap<>(); int count = 0;int left = i, right = i;while (right + footlen <= slen) {String in = s.substring(right, right + footlen);map2.put(in, map2.getOrDefault(in,0) + 1);if (map2.get(in) <= map.getOrDefault(in, 0)) {count++;}if (right - left + 1 > len * footlen) {String out = s.substring(left, left + footlen); if (map2.get(out) <= map.getOrDefault(out, 0)) {count--;}map2.put(out, map2.get(out) - 1);left += footlen;}if (count == len) {list.add(left);}right += footlen;}}return list;}
}

2.8 最小覆盖字串

在这里插入图片描述
76. 最小覆盖子串

方法一: 暴力枚举。
从第一个字母开始,找到含子串的字符串。记录起始位置及长度。再从第二个字符开始,找到含子串的字符串,若长度小于已记录的则更新长度,起始位置。

方法二:滑动窗口。
为不失一般性,使用一条线代表 s 字符串。
在这里插入图片描述
当移动到【left,right】划定的字符串中含有子串后,若继续右移right,进入窗口,那么长度变大,与所求相悖。因此,向右移动 left,进行出窗口操作。若【left,right】划定范围仍满足要求则 更新 起始位置和长度。当 不满足要求时,移动 right 进窗口,至符合要求或到达 s 字符串尾。

优化:假定使用 map 记录,字符出现的次数。但是这样在判定是否含有子串时,要对 t 的 map 和 【left,right】的 map 一一取出再比较。这样很浪费时间。这里我们引入一个 count 变量 来记录 【left,right】中合法字符的种类(某一字符数量等于 t 中的数量则 count++) (不是记个数的原因:主要是 eg:t: abcd,数量为 4,【left,right】:aaaa,数量同样为 4,且 a 是 t 中字符。)
当 count == tmap.size() 时说明符合要求了。

class Solution {public String minWindow(String s, String t) {int slen = s.length(), tlen = t.length();if (slen < tlen) {return "";}Map<Character, Integer> tmap = new HashMap<>();for (char c : t.toCharArray()) {tmap.put(c, tmap.getOrDefault(c, 0) + 1);}Map<Character, Integer> smap = new HashMap<>();int left = 0, right = 0, begin = -1, count = 0, minLen = Integer.MAX_VALUE;while (right < slen) {char c = s.charAt(right);// 仅处理 t 中存在的字符smap.put(c, smap.getOrDefault(c, 0) + 1);// 当该字符的数量满足 t 中的要求时,count 加 1if (smap.get(c).equals(tmap.getOrDefault(c,0))) {count++;}// 当窗口包含 t 中所有字符时,尝试收缩左指针while (count == tmap.size() && left <= right) {// 更新最小窗口if (right - left + 1 < minLen) {begin = left;minLen = right - left + 1;}char leftChar = s.charAt(left);// 处理左指针指向的字符(若在 t 中)smap.put(leftChar, smap.get(leftChar) - 1);// 若数量不满足 t 的要求,count 减 1(退出收缩循环)if (smap.getOrDefault(leftChar,0) < tmap.getOrDefault(leftChar,0)) {count--;}left++; // 移动左指针}right++; // 移动右指针}return begin == -1 ? "" : s.substring(begin, begin + minLen);}
}

再次优化,据题目可知 s,t 中全部是英文字母,所以可以用 int【128】 的数组来代替 map。

class Solution {public String minWindow(String ss, String tt) {char[] s = ss.toCharArray();char[] t = tt.toCharArray();int[] hasht = new int[128];int category = 0;for (char ret : t) {if (hasht[ret]++ == 0)category++;}int begin = -1, min = Integer.MAX_VALUE;int[] hashs = new int[128];for (int right = 0, left = 0, count = 0; right < s.length; right++) {char in = s[right];if (++hashs[in] == hasht[in])count++;while (count == category) {if (right - left + 1 < min) {begin = left;min = right - left + 1;}char out = s[left++];if(hashs[out]-- == hasht[out]) count--;}}return begin == -1 ? "" : ss.substring(begin, begin+min);}
}

文章转载自:

http://PrFBSf0W.mddLx.cn
http://6CIVzM0p.mddLx.cn
http://bHRDjIKU.mddLx.cn
http://cLJqP1nD.mddLx.cn
http://t6ICDQXy.mddLx.cn
http://3y3mD5pj.mddLx.cn
http://4iOlgmEM.mddLx.cn
http://So2ul6Dy.mddLx.cn
http://DPd8ngzR.mddLx.cn
http://rN0egBM7.mddLx.cn
http://oVXjxiVw.mddLx.cn
http://yUH0XM7B.mddLx.cn
http://iMDv2LZJ.mddLx.cn
http://tTIsISMc.mddLx.cn
http://XfV5R5Jn.mddLx.cn
http://pVd1teWQ.mddLx.cn
http://dshf631y.mddLx.cn
http://VunsosM3.mddLx.cn
http://1O0yeezv.mddLx.cn
http://qCL4dXEu.mddLx.cn
http://g7SFCH00.mddLx.cn
http://Iy9ebEWe.mddLx.cn
http://YKst30Ei.mddLx.cn
http://bYsKLwFc.mddLx.cn
http://E6l1QdER.mddLx.cn
http://XvZuPmGl.mddLx.cn
http://nCgMd1R8.mddLx.cn
http://b2jDj6rb.mddLx.cn
http://qGdCNKjf.mddLx.cn
http://1QnBhqmq.mddLx.cn
http://www.dtcms.com/a/378300.html

相关文章:

  • MongoDB 在线安装-一键安装脚本(CentOS 7.9)
  • DeepSeek辅助编写的利用quick_xml把xml转为csv的rust程序
  • Rider中的Run/Debug配置对应的本地文件
  • 综合项目实践:基于基础语法核心的Python项目
  • 开始 ComfyUI 的 AI 绘图之旅-Flux.1图生图(八)
  • 供应商管理系统包含哪些模块?
  • MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
  • Apache服务——搭建实验
  • “一半是火焰,一半是海水”,金融大模型的爆发与困局
  • 开源 C++ QT Widget 开发(十六)程序发布
  • MPC控制器C语言实现:基于一阶RL系统
  • C++版单例模式-现代化简洁写法
  • 强大的开源文档问答工具-Kotaemon
  • 音视频学习(六十三):AVCC和HVCC
  • 深度解析强化学习(RL):原理、算法与金融应用
  • 独立显卡和集成显卡切换电脑卡住了怎么办?
  • 加固笔记本是什么意思?加固笔记本图片
  • 光子精密3D线激光轮廓测量仪:赋能手机生产全流程质量与效率升级
  • springboot excel 表格入门与实战
  • react实现无缝轮播组件
  • DbGate数据库管理新方案:cpolar打造跨平台远程访问通道
  • Spark+Hive中间件
  • 【案例分享】TeeChart 助力 Softdrill 提升油气钻井数据可视化能力
  • 在图形 / 游戏开发中,为何 Pixels Per Unit(PPU)数值越小,物体在屏幕上显示的尺寸越大?
  • new和mallo的区别
  • mysql中%前置模糊查询怎么优化
  • 单串口服务器-工业级串口联网解决方案
  • 使用 Tkinter + Requests 实现地理信息安全系统学习时长助手
  • 多语言共享贩卖机投资理财共享售卖机投资理财系统
  • 京东JDS 测评图形规律题答题技巧