Day84(滑动窗口)

本文深入探讨了三种基于滑动窗口的字符串处理问题:无重复字符的最长子串、最小覆盖子串以及字符串排列检查。通过示例代码解析了如何使用滑动窗口在不同场景下找到满足条件的子串,强调了窗口收缩和扩大的关键细节,以及在处理过程中对窗口大小限制的不同策略。这些算法在字符串处理和信息检索中具有广泛应用。
摘要由CSDN通过智能技术生成

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

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

注意细节:
① 因为k-v    k:字符 v:字符出现的索引位置。所以收缩窗口left是根据重复的字符的位置来,并不是直接++
②left根据字符索引出现的位置上+1,并且不能后退。故left=Math.max(left,map.get(c)+1)【例子:abba 】
③ max=Math.max(max,right-left);//不能写在if条件里,因为当s.length==1的时候,就只有一个字符,那么这时候如果写在if里,就没有计算窗口的长度了 

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==0||s==null) return 0;
        Map<Character,Integer>map=new HashMap<>();//k-v k:字符 v:字符出现的索引位置
        int left=0,right=0;
        int max=Integer.MIN_VALUE;
        while(right<s.length()){
            char c=s.charAt(right);
            if(map.containsKey(c)){
                //abba left根据字符索引出现的位置上+1,并且不能后退
                left=Math.max(left,map.get(c)+1);//收缩窗口是根据重复的字符的位置来,并不是直接++
                
            }
            map.put(c,right);
            right++;
            max=Math.max(max,right-left);//不能写在if条件里,因为当s.length==1的时候,就只有一个字符,那么这时候如果写在if里,就没有计算窗口的长度了

        }
        return max;
    }
}

76、最小覆盖子串(此题是在567的固定窗口下进阶为对窗口大小不进行限制)

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

缩小窗口时,先判断后键值缩小,扩大窗口是,先键值增大后判断

//扩大窗口的
if (map.containsKey(c)) {
   // window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入window,只需要将map中的加入即可
   window.put(c, window.getOrDefault(c, 0) + 1);
   if (map.get(c).equals(window.get(c))) {
      sum++;
   }
}
//缩小窗口的
if (map.containsKey(temp)) {
   if (map.get(temp).equals(window.get(temp))) {
      sum--;
   }
   window.put(temp, window.getOrDefault(temp, 0) - 1);
   
}

class Solution {
    public String minWindow(String s, String t) {
        Map<Character, Integer> map = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();
        for (char c : t.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        int left = 0, right = 0;
        int sum = 0;
        int start = 0;//start表示符合最优解的substring的起始位序
        int len = Integer.MAX_VALUE;//len用来记录最终窗口的长度,并且以len作比较,淘汰选出最小的substring的len

        while (right < s.length()) {
            char c = s.charAt(right);
            right++;

            if (map.containsKey(c)) {
                // window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入window,只需要将map中的加入即可
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (map.get(c).equals(window.get(c))) {
                    sum++;
                }
            }
            //满足条件的窗口 这里不是固定窗口,对窗口大小不限制,当map和window中的数据一致,满足条件,开始收缩
            while (sum == map.size()) {
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                char temp = s.charAt(left);
                left++;
                if (map.containsKey(temp)) {
                    if (map.get(temp).equals(window.get(temp))) {
                        sum--;
                    }
                    //window.put(d,window.get(temp)-1);——bug:若一进去就将window对应的键值缩小,就永远不会满足下面的if,
                    // while也会一直执行,那么left越界,
                    // 因此,尽管和上面对窗口的处理几乎一样,但是这个处理的顺序还是很关键的!要细心!
                    window.put(temp, window.getOrDefault(temp, 0) - 1);
                    //故:缩小窗口时,先判断后键值缩小,扩大窗口是,先键值增大后判断
                }
            }
        }
        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
    }
}

567、字符串的排列

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。换句话说,s1 的排列之一是 s2 的 子串 。

 76、438、567三题是一样的模板,可以有同样的思路

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        Map<Character, Integer> map = new HashMap<>();
        Map<Character, Integer> window = new HashMap<>();
        for (char c : s1.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        int left = 0, right = 0;
        int sum = 0;
        while (right < s2.length()) {
            char c = s2.charAt(right);
            right++;
            if (map.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (map.get(c).equals(window.get(c))) {
                    sum++;
                }
            }
            
            while (right - left >= s1.length()) {
                if (sum == map.size()) {
                    return true;
                }
                char ch = s2.charAt(left);
                left++;
                //right与left只隔了s1.length的距离,
                //所以当boa时,right在a这里,left在b这里,这时候可以执行sum--。所以boa最后会返回false
                if (map.containsKey(ch)) {
                    if (map.get(ch).equals(window.get(ch))) {
                        sum--;
                    }
                    window.put(ch, window.getOrDefault(ch, 0) - 1);
                }
            }
        }
        return false;
    }
        
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值