leetcode hot100 滑动窗口&子串

滑动窗口

3. 无重复字符的最长子串

题解:

  • 最初采用的是哈希表加双重循环遍历从每个索引开始的最长字符串, 但这样的时间空间复杂度都特别高, 仔细观察有可以优化的地方

  • 改双层循环为单层循环, 不再遍历从每个索引开始的最长字符串, 观察可得当遇到与前面字符串重复的字符#时, 从前面重复的字符#处开始即可

java 复制代码
public int lengthOfLongestSubstring(String s) {
        if(s.length() == 0 || s==null){
            return 0;
        }
        Map<Character, Integer> map = new HashMap<>();
        int max = 0;
        for(int i = 0, j = 0; j < s.length(); j++){
            while(map.containsKey(s.charAt(j))){
                map.remove(s.charAt(i));
                i++;
            }
            map.put(s.charAt(j),j);
            max = Math.max(max,j-i+1);

            // for(int j = i + 1; j < s.length(); j++){
            //     if(map.containsKey(s.charAt(j))){
            //         i = map.get(s.charAt(j));
            //         break;
            //     }else{
            //         map.put(s.charAt(j), j);
            //         temp++;
            //     }
            // }
        }
        return max;
    }

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

题解:

  • 查找异位词, 如果用哈希表比较麻烦, 因为涉及该字符串出现的随机顺序,

  • 由于出现的只会是小写字符, 那么可以通过$-'a'来将字符判断转换为在int[26]的数组里判断出现次数

  • 首先判断两数组头部字符串是否相等--通过获取字符串第i个位置字符串-'a'的索引即可知道该字符出现频率

  • 然后在长度为s.length-p.length范围内不断以1单位滑动窗口即可

java 复制代码
public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        int sLen = s.length();
        int pLen = p.length();
        if (sLen < pLen) {
            return new ArrayList<>();
        }
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; i++) {
            sCount[s.charAt(i) - 'a']++;
            pCount[p.charAt(i) - 'a']++;
        }
        if(Arrays.equals(sCount, pCount)){
            res.add(0);
        }
        for(int i = 0; i<sLen-pLen;i++){
            sCount[s.charAt(i) - 'a']--;
            sCount[s.charAt(i+pLen) - 'a']++;
            if(Arrays.equals(sCount, pCount)){
                res.add(i+1);
            }
        }
        return res;
    }

子串

560. 和为 K 的子数组

题解:

  • 方法一循环遍历每一个索引,计算子串即可, 时间复杂度O(n^2)

  • 方法二, 使用前缀和, 一次遍历记录数组头到索引i的子串和, 并使用哈希表来存储该值

  • 那么每到一个索引, 若该位置的前缀和p[j] 与前面位置某一索引i的前缀和p[i]只差为k, 那就是从i+1~j的子串和为k

  • 采用哈希表来存储该位置的前缀和, key为前缀和, 由于数组的值可以是正数也可是负数, 会存在多个位置的前缀和为同一值, 那么value可以记录为该前缀和一共记录的次数

  • 每遍历到一索引, 若该前缀和p[j]-k的值p[temp]存在于哈希表中, 那么子串的个数即为map.get(pre-k)个.

java 复制代码
public int subarraySum(int[] nums, int k) {
        // int count = 0,sum=0;
        // for(int i=0;i<nums.length;i++){
        //     sum = 0;
        //     for(int j = i;j<nums.length;j++){
        //         sum += nums[j];
        //         if(sum == k){
        //             count++;
        //         }
                
        //     }
        // }
        // return count;

        int count = 0,sum=0;
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        for(int i = 0;i<nums.length;i++){
            sum+=nums[i];
            if(map.containsKey(sum-k)){
                count+=map.get(sum-k);
            }
            map.put(sum,map.getOrDefault(sum,0)+1);
        }
        return count;
    }

239. 滑动窗口最大值

题解:

  • 暴力解法在数组以及k特别大时会运行超时

  • 通过暴力解法观察, 其实可以在k次比较最大值的地方优化, 如果新加入队列的值比队尾的值大, 那么此时该窗口的最大值就一定不是队尾的值了(至少都是新加入队列的值),

  • 所以在元素加入队列时可以进行判断, 如果当前值>队尾元素, 移除队尾元素, 加入当前值; 如果当前值< 队尾元素, 仍需加入当前值(如果不加入当前值, 经过一段时间的滑动, 无法保证队尾元素在窗口中)

  • 当需要判断当前窗口的最大值时, 从队列头获取即可, 通过队列存储的索引值来判断当前索引是否在窗口中,不在则移除直到找到元素

  • 如何保证靠近队头的元素即窗口中最大值--窗口中最大值>不在窗口中的队尾元素时会移除队尾元素, 窗口中最大值<不在窗口中的队尾元素时会直接添加至队尾;

  • 窗口中最大值>在窗口中的队尾元素时, 也会移除队尾元素, 窗口中非最大值遇到前方的窗口最大值时会添加在队尾

java 复制代码
public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        Deque<Integer> deque = new LinkedList<>();
        for(int i = 0;i<k;i++){
            while(!deque.isEmpty()&&nums[i]>=nums[deque.peekLast()]){
                deque.pollLast();
            }
            deque.offerLast(i);
        }
        int[] res = new int[n-k+1];
        res[0] = nums[deque.peekFirst()];
        for(int i = k;i<n;i++){
            while(!deque.isEmpty()&&nums[i]>=nums[deque.peekLast()]){
                deque.pollLast();
            }
            deque.offerLast(i);
            while(deque.peekFirst()<=i-k){
                deque.pollFirst();
            }
            res[i-k+1] = nums[deque.peekFirst()];
        }
        return res;
    }

76. 最小覆盖子串

题解:

  • 用一个哈希表*HashMap<Character, Integer>*来记录滑动窗口需要的n字符的数量,对于该哈希表的值,可以分为以下三种情况:

    • HashMap<Character, Integer>
    • Integer<0 -> 滑动窗口还需要字符的数量
    • Integer==0 滑动窗口刚好包括字符的数量
    • Integer>0 滑动窗口超过n字符的数量
  • 用一个变量V来记录当前已组成的字符串长度, 右指针开始移动,当遇到哈希表中存在的字符时:

    1. 如果该字符在哈希表中的值小于0,说明是滑动窗口缺少的字符,V++
    2. 递增哈希表的值
  • 当V==字符串t的长度时,代表该滑动窗口包含了t字符串的所有字符(这时候可以发现哈希表中的所有值都是大于等于0的),右指针停止移动。记录此时左右指针的差值,就是当前滑动窗口的长度,取最小值。

  • 此时,左指针开始移动,争取使滑动窗口长度变得更小,当遇到哈希表中存在的字符时:

    1. 递减哈希表的值
    2. 如果该字符在哈希表中的值小于0,说明滑动窗口缺少了当前左指针对应的字符,V--
java 复制代码
public String minWindow(String s, String t) {
        if (s.length() < t.length()) {
            return "";
        }
        HashMap<Character, Integer> count = new HashMap<>();
        // 统计组成t字符串的每个字符数量
        // count[n]<0:滑动窗口缺少多少个n字符
        // count[n]==0:滑动窗口刚好包含多少个n字符
        // count[n]>0:滑动窗口超过多少个n字符
        for (char c : t.toCharArray()) {
            count.put(c, count.getOrDefault(c, 0) - 1);
        }

        int formed = 0; // 已形成的字符数量
        int start = 0; // 记录最小覆盖子串的起始位置
        int length = Integer.MAX_VALUE; // 记录最小覆盖子串的长度

        for (int left = 0, right = 0, required = t.length(); right < s.length(); right++) {
            char c = s.charAt(right);
            // 更新窗口中的字符计数
            if (count.containsKey(c)) {
                if (count.get(c) < 0) {
                    formed++;
                }
                count.put(c, count.get(c) + 1);
            }

            // 当窗口中的字符满足条件时,尝试缩小窗口
            while (formed == required) {
                if (right - left + 1 < length) {
                    start = left;
                    length = right - left + 1;
                }

                char d = s.charAt(left);
                left++;

                if (count.containsKey(d)) {
                    count.put(d, count.get(d) - 1);
                    if (count.get(d) < 0) {
                        formed--;
                    }
                }
            }
        }
        return length == Integer.MAX_VALUE ? "" : s.substring(start, start + length);
    }
相关推荐
Dizzy.5178 分钟前
数据结构(查找)
数据结构·学习·算法
分别努力读书3 小时前
acm培训 part 7
算法·图论
武乐乐~3 小时前
欢乐力扣:赎金信
算法·leetcode·职场和发展
'Debug3 小时前
算法从0到100之【专题一】- 双指针第一练(数组划分、数组分块)
算法
Fansv5873 小时前
深度学习-2.机械学习基础
人工智能·经验分享·python·深度学习·算法·机器学习
yatingliu20195 小时前
代码随想录算法训练营第六天| 242.有效的字母异位词 、349. 两个数组的交集、202. 快乐数 、1. 两数之和
c++·算法
uhakadotcom5 小时前
Google DeepMind最近发布了SigLIP 2
人工智能·算法·架构
sjsjs115 小时前
【数据结构-并查集】力扣1202. 交换字符串中的元素
数据结构·leetcode·并查集
三年呀5 小时前
计算机视觉之图像处理-----SIFT、SURF、FAST、ORB 特征提取算法深度解析
图像处理·python·深度学习·算法·目标检测·机器学习·计算机视觉
淡黄的Cherry6 小时前
istio实现灰度发布,A/B发布, Kiali网格可视化(二)
java·算法·istio