LeetCode——滑动窗口

本文介绍了如何使用滑动窗口算法解决四个与字符串相关的问题:最小覆盖子串、字符串排列检查、字母异位词查找和无重复字符的最长子串,涉及字符计数、窗口扩展与收缩策略。

滑动窗口

包含特定元素的子串(要匹配到的目标),或最长[这个好像没啥意思]、或最短、或等长

思考:(暂时感受)

1)什么时候扩充窗口——串没走完就得扩呀;

2)窗口扩充后要更新哪些东西——窗口包括的内容,毕竟新来人了,东西多了;

3)什么时候缩小窗口——得看目标条件是啥了,最短的情况吧只要目标条件够了就得收缩,等长的情况吧等长就得缩噻;

4)窗口缩小后要更新哪些东西——窗口包括的内容;

5)什么时候更新结果——收缩的时候,还是一轮结束的时候。

1. 最小覆盖子串 76 简单

class Solution {
    public String minWindow(String s, String t) {
        // 两个Map记录目标字符以及窗口包含的字符
        Map<Character, Integer> need = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();

        // 填充目标字符的Map
        for (char c: t.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }

        // 设置左右指针,区间范围是左闭右开,所以刚开始区间内没有元素
        int left = 0;
        int right = 0;
        int valid = 0;  // 窗口中满足需要的字符个数
        // 记录最小覆盖子串的起始索引和长度
        int start = 0;
        int len = Integer.MAX_VALUE;

        // 扩大窗口至覆盖子串
        while (right < s.length()) {
            char c = s.charAt(right);
            // 扩大窗口
            right ++;
            // 对窗口内的数据进行更新
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (window.get(c).equals(need.get(c))) {
                    // 窗口内某个字符满足个数要求了
                    valid ++;
                }
            }

            // 缩小窗口仍覆盖子串
            while (valid == need.size()) {
                // 更新最小覆盖子串
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                // 缩小窗口
                char d = s.charAt(left);
                left ++;
                // 对窗口内的数据进行更新
                if (need.containsKey(d)) {
                    if (window.get(d).equals(need.get(d))) {
                        valid --;
                    }
                    window.put(d, window.get(d) - 1);
                }
            }

        }
        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);

    }
}

2. 字符串的排列 567 中等

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        // 思路:缩到最小覆盖,长度如果与s1相等,说明s1的排列之一是s2的子串
        // Map记录数量
        Map<Character, Integer> need = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();
        // 填充need
        for (char c : s1.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }
        // 滑动窗口
        int left = 0;
        int right = 0;
        // int start = 0;
        // int len = Integer.MAX_VALUE;
        int valid = 0;
        while (right < s2.length()) {
            char c = s2.charAt(right);
            // 扩大窗口
            right ++;
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (window.get(c).equals(need.get(c))) {
                    valid ++;
                }
            } 
            while (right - left >= s1.length()) {
            // while (valid == need.size()) {
                // if (right - left < len) {
                //     start = left;
                //     len = right - left;
                // }
                // 判断是否找到了合适的子串
                if (valid == need.size()) {
                    return true;
                }
                char d = s2.charAt(left);
                left ++;
                if (need.containsKey(d)) {
                    if (window.get(d).equals(need.get(d))) {
                        valid --;
                    }
                    window.put(d, window.get(d) - 1);
                }
            } 
            // if (len == s1.length()) {
            //     return true;
            // }
        }
        return false;
    }
}

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

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 存放结果
        List<Integer> result = new ArrayList<>();
        // 存放窗口元素
        Map<Character, Integer> need = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();
        for (char c : p.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }
        // 滑动窗口的左右指针
        int left = 0;
        int right = 0;
        int valid = 0;
        while (right < s.length()) {
            char c = s.charAt(right);
            // 扩大窗口
            right ++;
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (need.get(c).equals(window.get(c))) {
                    valid ++;
                }
            }
            while (right - left >= p.length()) {
                if (valid == need.size()) {
                    result.add(left);
                    // // window和valid要清空
                    // window.clear();
                    // valid = 0;
                }
                char d = s.charAt(left);
                left ++;
                if (need.containsKey(d)) {
                    if (window.get(d).equals(need.get(d))) {
                        valid --;
                    }
                    window.put(d, window.get(d) - 1);
                }
            }
        }
        return result;
    }
}

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

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 结果
        int result = 0;
        // 滑动窗口:重要的是如果map中键的值超过2,那就说明需要缩小窗口了
        Map<Character, Integer> window = new HashMap<>();
        int left = 0;
        int right = 0;
        while (right < s.length()) {
            char c = s.charAt(right);
            right ++;
            window.put(c, window.getOrDefault(c, 0) + 1);
            while (window.get(c) > 1) {
                char d = s.charAt(left);
                left ++;
                window.put(d, window.get(d) - 1);
            }
            // 更新答案
            result = Math.max(result, right - left);
        }
        return result;
    }
}

【未完待续……】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值