【面试算法题总结09】滑动窗口算法

滑动窗口算法:

 

例题1:最小覆盖子串

这里注意一个细节:Integer值的比较一定要用.equals()方法,而不是==

class Solution {
    public String minWindow(String s, String t) {
        Map<Character,Integer> need=new HashMap<>();
        Map<Character,Integer> window=new HashMap<>();
        for(int i=0;i<t.length();++i){
            need.put(t.charAt(i),need.getOrDefault(t.charAt(i),0)+1);
        }
        int left=0,right=0;
        //valid不是记录t中多少个字符在window里面符合条件了,而是记录t中多少种字符在window里面符合条件了。是“种”
        int valid=0;
        //记录最小覆盖子串的起始索引及长度
        int start=left,len=Integer.MAX_VALUE;
        //开始滑动
        while(right<s.length()){
            char r=s.charAt(right);
            ++right;
            //进行滑动后的数据更新
            if(need.containsKey(r)){
                window.put(r,window.getOrDefault(r,0)+1);
                if(window.get(r).equals(need.get(r))){
                    ++valid;
                }
            }
            //符合条件时,开始滑动左端点
            while(valid==need.size()){
                //在符合条件的情况中进行筛选
                if(right-left<len){
                    len=right-left;
                    start=left;
                }
                //开始滑动
                char l=s.charAt(left);
                ++left;
                //进行滑动后的数据更新
                if(need.containsKey(l)){
                    if(window.get(l).equals(need.get(l))){
                        --valid;
                    }
                    window.put(l,window.get(l)-1);
                }
            }
        }
        return len==Integer.MAX_VALUE?"":s.substring(start,start+len);
    }
}

 

例题2:字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        Map<Character,Integer> window=new HashMap<>();
        Map<Character,Integer> need=new HashMap<>();
        //将p的所有字符放入need
        for(int i=0;i<s1.length();++i){
            char c=s1.charAt(i);
            need.put(c,need.getOrDefault(c,0)+1);
        }
        int left=0,right=0;
        int valid=0;
        //开始滑动
        while(right<s2.length()){
            char r=s2.charAt(right);
            ++right;        //right已经是r所在位置+1了
            //进行滑动后的数据更新
            if(need.containsKey(r)){
                window.put(r,window.getOrDefault(r,0)+1);
                if(window.get(r).equals(need.get(r))){
                    ++valid;
                }
            }
            //符合条件时,开始滑动左端点
            while(right-left>=s1.length()){
                //在符合条件的情况下,进行筛选
                if(valid==need.size()){
                    return true;
                }
                char l=s2.charAt(left);
                ++left;
                //进行滑动后的数据更新
                if(need.containsKey(l)){
                    if(window.get(l).equals(need.get(l))){
                        --valid;
                    }
                    window.put(l,window.get(l)-1);
                }
            }
        }
        return false;
    }
}

 

例题3:找到字符串中所有字母异位词

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> result=new ArrayList<>();
        Map<Character,Integer> window=new HashMap<>();
        Map<Character,Integer> need=new HashMap<>();
        //将p的所有字符放入need
        for(int i=0;i<p.length();++i){
            char c=p.charAt(i);
            need.put(c,need.getOrDefault(c,0)+1);
        }
        int left=0,right=0;
        int valid=0;
        //开始滑动
        while(right<s.length()){
            char r=s.charAt(right);
            ++right;        //right已经是r所在位置+1了
            //进行滑动后的数据更新
            if(need.containsKey(r)){
                window.put(r,window.getOrDefault(r,0)+1);
                if(window.get(r).equals(need.get(r))){
                    ++valid;
                }
            }
            //符合条件时,开始滑动左端点
            while(right-left>=p.length()){
                //在符合条件的情况下,进行筛选
                if(valid==need.size()){
                    result.add(left);
                }
                char l=s.charAt(left);
                ++left;
                //进行滑动后的数据更新
                if(need.containsKey(l)){
                    if(window.get(l).equals(need.get(l))){
                        --valid;
                    }
                    window.put(l,window.get(l)-1);
                }
            }
        }
        return result;
    }
}

 

例题4:无重复字符的最长子串

解法1:滑动窗口算法

window存储子串中字符以及对应数量
时间复杂度:O(n)
空间复杂度:O(1)。字符的 ASCII 码范围为 0 ~ 127 ,哈希表 window最多使用 O(128) = O(1)大小的额外空间。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character,Integer> window=new HashMap<>();
        int left=0,right=0;
        int len=0;
        while(right<s.length()){
            char r=s.charAt(right);
            ++right;
            window.put(r,window.getOrDefault(r,0)+1);
            while(window.get(r)>1){
                char l=s.charAt(left);
                ++left;
                window.put(l,window.get(l)-1);
            }
            if(right-left>len){
                len=right-left;
            }
        }
        return len;
    }
}

解法2:动态规划算法

map统计各字符最后一次出现的索引位置。也可以不使用map,而是线性搜索。这样时间复杂度就是O(n^2)
时间复杂度:O(n)
空间复杂度:O(n)。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==0){
            return 0;
        }
        //dp[i]表示以s.charAt(i)结尾的最长不重复字符子串长度
        int[] dp=new int[s.length()];
        dp[0]=1;
        //map统计各字符最后一次出现的索引位置
        Map<Character,Integer> map=new HashMap<>();
        map.put(s.charAt(0),0);
        //构建过程中得到最大值result
        int result=1;
        //开始构建dp数组
        for(int right=1;right<s.length();++right){
            int left = map.getOrDefault(s.charAt(right), -1);
            map.put(s.charAt(right), right); // 更新哈希表
            if(dp[right-1]<right-left){		//当有重复字符的子串长度-1大于以s.charAt(right-1)为结尾的最长不重复子串长度时,为直接长度+1
                dp[right]=dp[right-1]+1;
            }else{	//否则,为有重复字符的子串长度-1
                dp[right]=right-left;
            }
            //记录最大值
            result = Math.max(result, dp[right]);
        }
        return result;
    }
}

解法3:动态规划算法+状态压缩

map统计各字符最后一次出现的索引位置。也可以不使用map,而是线性搜索。这样时间复杂度就是O(n^2)
时间复杂度:O(n)
空间复杂度:O(1)。字符的 ASCII 码范围为 0 ~ 127 ,哈希表map最多使用 O(128) = O(1)大小的额外空间。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //dp[i]表示以s.charAt(i)结尾的最长不重复字符子串长度。其状态压缩后为temp变量
        int temp=0;
        //map统计各字符最后一次出现的索引位置
        Map<Character,Integer> map=new HashMap<>();
        //构建过程中得到最大值result
        int result=0;
        //开始构建dp数组
        for(int right=0;right<s.length();++right){
            int left = map.getOrDefault(s.charAt(right), -1);
            map.put(s.charAt(right), right); // 更新哈希表
            if(right-left>temp){
                temp=temp+1;
            }else{
                temp=right-left;
            }
            //记录最大值
            result = Math.max(result, temp);
        }
        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值