LeetCode 热题 HOT 100 -------3. 无重复字符的最长子串(滑动窗口) 438. 找到字符串中所有字母异位词 (滑动窗口) 76. 最小覆盖子串 (滑动窗口)

dasdas在这里插入图片描述

//解题思路:典型的“滑动窗口”解题,思想就是用哈希表(表示队列思想)),遍历字符串,每个字符作key,而我们的字符对应的位置作value,每次放入前应该判别有没有这个key,如果有我们就 从这个值的下一个字符开始算起(map.containsKey(s.charAt(i))),与遍历到i时的最大字符串长度为Math.max( max,i-left+1)

//一直维持这样的队列,找出队列出现最长的长度时候,求出解!
class Solution {
    public int lengthOfLongestSubstring(String s) {
        //如果字符串长度为0,则返回0
        if(s.length()==0){
            return 0;
        }
        //定义返回的最大值以及当前重复字符的位置
        int max = 0;
        int left = 0;
        //定义哈希表
        Map<Character,Integer> map = new HashMap<Character,Integer>();
        //遍历字符串
        for(int i = 0 ; i< s.length() ; i++){
            if(map.containsKey(s.charAt(i))){
                //说明包含了重的,我们开始从重复的字符的位置的下一个字符开始记
                left = Math.max(left,map.get(s.charAt(i))+1);
            }
            //如果不包含我们就把字符放入
            map.put(s.charAt(i),i);
            //然后更新最大值
            max = Math.max(max,i-left+1);
        }
        return max;
    }
}

dsadas在这里插入图片描述

//看到这个题,我们可以用尝试考虑”滑动窗口“,然后辅助用 数组或者指针
//首先看到是否含有相同的字母,我们可以通过ASCI码来转换
//思路1:
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length() , pLen = p.length();
        //返回的数组
        List<Integer> res = new ArrayList<>();
        //用数组来表达字符串"p"
        int[] pArray = new int[26];
        for(int i = 0 ; i<pLen ; i++){
            pArray[p.charAt(i)-'a']++;
        }
        //pArray中包含的就是p中所有的字符
        int[] sArray = new int[26];
        //左右指针
        int l = 0 , r = 0;
        while(r<sLen){
            sArray[s.charAt(r)-'a']++;  //将s字符串转换成对应数组中的值
            if((r-l+1)==pLen){
                if(Arrays.equals(sArray,pArray)){
                    res.add(l);
                }
                sArray[s.charAt(l)-'a']--;
                l++;
            }
            r++;
        }
        return res;
    }
}

// 思路2:
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        int[] sArray = new int[26];
        int[] pArray = new int[26];
        List<Integer> res = new ArrayList<Integer>();
        for(int i=0 ; i<pLen ; i++){
            pArray[p.charAt(i)-'a']++;
        }
        int l = 0 ;
        for(int r=0; r<sLen ; r++ ){
            int curR = s.charAt(r)-'a';
            sArray[curR]++; 
            while(sArray[curR]>pArray[curR]){ //说明窗口中相同的字母多了,则使左下标增大
                int curL = s.charAt(l)-'a';
                sArray[curL]--;
                l++;
            }
            if(r-l+1 ==pLen){
                res.add(l);
            }
        }
        return res;
    }
}

dasdas在这里插入图片描述

class Solution {
    public String minWindow(String s, String t) {
        //先写判别条件
        if(s == null || t == null || s == "" || t =="" || s.length() < t.length()){
            return "";
        }
        //维护两个数组,记录已有字符串指定字符的出现次数,和目标字符串指定字符出现的次数
        //ASCII表总长128
        int[] need = new int[128];
        int[] have = new int[128];

        //将目标字符串指定字符的出现记录
        for(int i = 0 ; i<t.length() ; i++){
            need[t.charAt(i)]++;
        }
        //分别为左指针,右指针,最小长度(初始值为一定不可到达的长度)
        //已有字符串中目标字符串指定字符的出现总频次以及最小覆盖子串在原字符串中的起始位置
        int left = 0 , right = 0 , min = s.length()+1 , count = 0 , start = 0;
        while(right < s.length()){
            char r = s.charAt(right);
            //说明该字符不被目标字符串需要,此时有两种情况
            // 1.循环刚开始,那么直接移动右指针即可,不需要做多余判断
            // 2.循环已经开始一段时间,此处又有两种情况
            //  2.1 上一次条件不满足,已有字符串指定字符出现次数不满足目标字符串指定字符出现次数,那么此时
            //      如果该字符还不被目标字符串需要,就不需要进行多余判断,右指针移动即可
            //  2.2 左指针已经移动完毕,那么此时就相当于循环刚开始,同理直接移动右指针    
            if(need[r] == 0){
                right++;
                continue;
            }
            //当且仅当已有字符串目标字符出现的次数小于目标字符串字符的出现次数时,count才会+1
            //是为了后续能直接判断已有字符串是否已经包含了目标字符串的所有字符,不需要挨个比对字符出现的次数
            if(have[r]<need[r]){
                count++;
            }
            //已有字符串中目标字符出现的次数+1
            have[r]++;
            //移动右指针
            right++;
             //当且仅当已有字符串已经包含了所有目标字符串的字符,且出现频次一定大于或等于指定频次
              while (count == t.length()) {
                //挡窗口的长度比已有的最短值小时,更改最小值,并记录起始位置
                if (right - left < min) {
                    min = right - left;
                    start = left;
                }
                char l = s.charAt(left);
                //如果左边即将要去掉的字符不被目标字符串需要,那么不需要多余判断,直接可以移动左指针
                if (need[l] == 0) {
                    left++;
                    continue;
                }
                //如果左边即将要去掉的字符被目标字符串需要,且出现的频次正好等于指定频次,那么如果去掉了这个字符,
                //就不满足覆盖子串的条件,此时要破坏循环条件跳出循环,即控制目标字符串指定字符的出现总频次(count)-1
                if (have[l] == need[l]) {
                    count--;
                }
                //已有字符串中目标字符出现的次数-1
                have[l]--;
                //移动左指针
                left++;
            }
        }
        //如果最小长度还为初始值,说明没有符合条件的子串
        if (min == s.length() + 1) {
            return "";
        }
        //返回的为以记录的起始位置为起点,记录的最短长度为距离的指定字符串中截取的子串
        return s.substring(start, start + min); 
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值