leetcode之滑动窗口刷题总结1

leetcode之滑动窗口刷题总结1

1-无重复字符的最长子串
题目链接:题目链接戳这里!!!

思路:这题是标准的滑动窗口,不过测试用例比较刁钻,需要考虑很多细节。
右侧窗口边界向右滑动,将字符串的值依次存入哈希表,如果出现重复的,则更新最大窗口和左侧窗口边界,若没出现重复,则只更新最大窗口。

AC代码如下:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.equals("")){
            return 0 ;
        }
        if(s.length()==1){
            return 1 ;
        }
        int left=0, right = 0;
        int max = 0 ;
        Map<Character,Integer> map = new HashMap<>() ;
        for(; right<s.length(); right++){
            if(map.containsKey(s.charAt(right)) && map.get(s.charAt(right))>=left){
                int pre = map.get(s.charAt(right)) ;
                max = Math.max(max, right-left) ;
                left=pre+1 ;
            }else{
                 max = Math.max(right-left + 1, max);
            }
                map.put(s.charAt(right), right) ;
               
        }
        return max ;
    }
}

在这里插入图片描述
2-存在重复元素II
题目链接:题目链接戳这里!!!

思路:滑动窗口+哈希表
右侧窗口依次向后滑动,哈希表存储元素,如果出现重复元素,则更新左侧窗口,同时比较窗口大小是否小于等于k即可。

AC代码如下:

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        int n = nums.length ;
        int right = 0 ;
        Map<Integer, Integer> map = new HashMap<>() ;
        for(; right<n; right++){
           if(map.containsKey(nums[right])){
               int left = map.get(nums[right]) ;
            if(nums[left]==nums[right] && Math.abs(right-left)<=k){
                return true ;
            }
           }
            map.put(nums[right], right) ;
        }
        return false ;
    }
}

在这里插入图片描述
3-至少有k个重复字符的最长子串
题目链接:题目链接戳这里!!!

思路:
递归最基本的是记住递归函数的含义(务必牢记函数定义):本题的 longestSubstring(s, k) 函数表示的就是题意,即求一个最长的子字符串的长度,该子字符串中每个字符出现的次数都最少为 k。函数入参 s 是表示源字符串;kk是限制条件,即子字符串中每个字符最少出现的次数;函数返回结果是满足题意的最长子字符串长度。

递归的终止条件(能直接写出的最简单 case):如果字符串 ss的长度少于 k,那么一定不存在满足题意的子字符串,返回 0;

调用递归(重点):如果一个字符 c 在 s 中出现的次数少于 k次,那么 s 中所有的包含 c 的子字符串都不能满足题意。所以,应该在 s 的所有不包含 c 的子字符串中继续寻找结果:把 s 按照 c 分割(分割后每个子串都不包含 c),得到很多子字符串 t;下一步要求 t 作为源字符串的时候,它的最长的满足题意的子字符串长度(到现在为止,我们把大问题分割为了小问题(s → t))。此时我们发现,恰好已经定义了函数 longestSubstring(s, k) 就是来解决这个问题的!所以直接把 longestSubstring(s, k) 函数拿来用,于是形成了递归。

未进入递归时的返回结果:如果 s 中的每个字符出现的次数都大于 k 次,那么 s 就是我们要求的字符串,直接返回该字符串的长度。

class Solution {
    public int longestSubstring(String s, int k) {
        if(s.length()<k){
            return 0 ;
        }
        Map<Character, Integer> count = new HashMap<>() ;
        for(int i=0; i<s.length(); i++){
            count.put(s.charAt(i), count.getOrDefault(s.charAt(i),0)+1) ;
        }
        for(char c : count.keySet()){
            if(count.get(c)<k){
                int res = 0 ;
                for(String t : s.split(String.valueOf(c))){
                    res = Math.max(res, longestSubstring(t,k)) ;
                }
                return res ;
            }
        }
        return s.length() ;
    }
}

在这里插入图片描述
4-替换后的最长重复字符
题目链接:题目链接戳这里!!!

注:这一题和上一题都是字节面试原题,这题的思路是滑动窗口+哈希表,每次将字符加入哈希表,并求出字符中出现次数最多的数量maxCnt,若窗口长度减去maxCnt大于k,则替换后不满足要求,需要维护左侧窗口。

class Solution {
    public int characterReplacement(String s, int k) {
        Map<Character,Integer> map =  new HashMap<>() ;
        int left = 0, right = 0, maxCnt = 0, res=0 ;
        for(;right<s.length(); right++){
            map.put(s.charAt(right), map.getOrDefault(s.charAt(right), 0)+1) ; //每次将字符放入
            maxCnt = Math.max(maxCnt, map.getOrDefault(s.charAt(right), 0)) ;//找出出现的最多的
            if(right-left+1-maxCnt>k){//替换两个不满足要求,调整窗口
                map.put(s.charAt(left), map.get(s.charAt(left))-1) ;
                left++ ;
            }
            res = Math.max(res, right-left+1) ;//最大窗口,也就是替换满足要求的字符串长度
        }
        return res ;
    }
}

在这里插入图片描述
5-最长重复子数组
题目链接:题目链接戳这里!!!

这题和最长重复子序列很像,可以用动态规划。
思路:找到递推表达式即可解出。
递推表达式如下:
dp[i][j] = dp[i-1][j-1]+1 ,当nums1[i]==nums2[j]
dp[i][j]=0,当nums1[i]!=nums2[j]
动态规划AC代码如下:

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int [][] dp = new int [nums1.length+1][nums2.length+1] ;
        int max = 0 ;
        for(int i=1; i<dp.length; i++){
            for(int j=1; j<dp[0].length; j++){
                if(nums1[i-1]==nums2[j-1]){
                    dp[i][j] = dp[i-1][j-1] + 1 ;
                    max = Math.max(max,dp[i][j]) ;
                }
            }
        }
        return max ;
    }
}

在这里插入图片描述

6-最长公共子序列
题目链接:题目链接戳这里!!!

为了和最长重复子数组做对比,我们看一下这个最长公共子序列,这个也是动态规划,子序列不要求连续,子数组是要求连续的。

递推表达式:
dp[i][j] = dp[i-1][j-1]+1 ,当nums1[i]==nums2[j]
dp[i][j]=max(dp[i-1][j],dp[i][j-1]),当nums1[i]!=nums2[j]

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length() ;
        int n = text2.length() ;
        int [][] dp = new int [m+1][n+1] ;
        int ans = 0 ;
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(text1.charAt(i)==text2.charAt(j)){
                   dp[i+1][j+1] = dp[i][j] + 1 ;
                }else{
                    dp[i+1][j+1] = Math.max(dp[i+1][j],dp[i][j+1]) ;
                }
                ans = Math.max(ans, dp[i+1][j+1]) ;
            }
        }
        return ans ;
    }
}

在这里插入图片描述
7-滑动窗口最大值
题目链接:题目链接戳这里!!!

先看一个常规的超时解法,785ms都超时,也是没谁了。

代码如下:

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length, i= 0 ;
        if(k==1){
            return nums;
        }
        int [] res = new int [n-k+1] ;
        int left = 0, right = k-1 ;
        for(;right<nums.length; right++){
            int max = max(nums,left,right) ;
            res[i++] = max ;
            left++ ;
        }
        return res ;
    }
    public int max(int [] nums, int left,  int right){
        int v = nums[left];
        for(int i=left+1; i<=right; i++){
            v = (v<=nums[i]) ? nums[i] : v ;
        }
        return v ;
    }
}

下面看一下AC代码:

单调队列解决滑动窗口问题:
我参照的这个,链接如下:单调队列解决解析戳这里!!!

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if(n<2 || nums==null){//数组长度为1或者为空,返回数组本身
            return nums ;
        }
        int [] res = new int [n-k+1] ; //要返回的数组
        int idx = 0 ;
        LinkedList<Integer> queue = new LinkedList<>() ; //单调队列
        for(int i=0; i<n; i++){
            if(!queue.isEmpty() && queue.peek()<=i-k){ //队首下标距离i的位置大于等于k,则出队
                queue.poll() ;
            }
            while(!queue.isEmpty() && nums[queue.peekLast()]<=nums[i]){
                queue.pollLast(); //对位元素小于当前元素,则出队
            }
            queue.addLast(i) ;
            if(i+1>=k){ //满足返回要求
                res[idx++] = nums[queue.peek()] ;
            }
        }
        return res ;
    }
}

在这里插入图片描述
8-字符串的排列
题目链接:题目链接戳这里!!!
思路:这题可以队s2进行截取s1长度的字符串,然后分别对s1和截取的s2进行排序,对比排序后的截取s2的字符串和字符串s1是否相等即可。

尽管可以通过,但是效率并不高,耗时超过1秒,竟然也能AC,哈哈

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int len = s1.length() ;
        for(int i=0;i<s2.length(); i++){
                if(i+len<=s2.length() && same(s1,s2.substring(i,i+len))){
                    return true ;
                }
        }
        return false ;
    }
    public boolean same(String s1, String s2){
        char [] a1 = s1.toCharArray() ;
        char [] a2 = s2.toCharArray() ;
        Arrays.sort(a1) ;
        Arrays.sort(a2) ;
        for(int i=0; i<a2.length; i++){
            if(a2[i] != a1[i]){
                return false ;
            }
        }
        return true ;
    }
}

在这里插入图片描述
使用滑动窗口法,效率会高一些,只需要一层循环即可,如果s1是s2的子串的排列,则s1中各元素的个数等于截取对应长度的s2中各元素的个数相同。

AC代码如下:

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //s1排列是s2的子串,则s1对应元素的个数与s2相等
        int n = s1.length() ;
        int m = s2.length() ;
        int [] cnt1 = new int [26] ;
        int [] cnt2 = new int [26] ;
        if(n>m){
            return false ;
        }
        for(int i=0; i<n; i++){
            cnt1[s1.charAt(i)-'a']++ ;
            cnt2[s2.charAt(i)-'a']++ ;
         }
         if(Arrays.equals(cnt1,cnt2)){
             return true ;
         }
         for(int i=n; i<m; i++){
             cnt2[s2.charAt(i)-'a']++ ;
             cnt2[s2.charAt(i-n)-'a']--;
             if(Arrays.equals(cnt1,cnt2)){
                 return true ;
             }
         }
         return false ;
    }
}

在这里插入图片描述
9-子数组最大平均数I
题目链接:

思想:前缀和+滑动窗口,就是区间和问题,再求个均值就可以,注意都要转换成double型。

AC代码如下:

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        int  right = k, sum=0;
        int [] sums = new int [nums.length] ;
        if(k==nums.length){
            for(int x : nums){
                sum += x ;
            }
            return 1.0 * sum / k ;
        }
         sums[0] = nums[0] ;
        for(int i=1; i<nums.length; i++){
            sums[i] = sums[i-1] +  nums[i] ;
        }
        double max = 1.0 * sums[k-1] / k ;
        for(;right<sums.length; right++){
            double v = 1.0 * (sums[right] - sums[right-k])/k ;
            max = Math.max(max, v) ;
        }
        return max ;
    }
}

在这里插入图片描述
10-存在重复元素III
题目链接:题目链接戳这里!!!

思路:这题直接暴力也能过,就是效率有点低。

AC代码如下:

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        int n = nums.length ;
        if(k>=10000){
            return false ;
        }
        for(int i=0; i<n; i++){
            for(int j=i+1; j<n; j++){
                if(Math.abs((long)((long)nums[i]-(long)nums[j]))<=t && Math.abs(i-j)<=k){
                    return true ;
                }
            }
        }
        return false ;
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值