leetcode之滑动窗口刷题总结1

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.equalsleetcode之滑动窗口算法小结

leetcode之哈希表刷题总结1

leetcode之深度优先搜索刷题总结3

算法总结之滑动窗口

Leetcode刷题100天—162. 寻找峰值(滑动窗口)—day39

Leetcode刷题100天—162. 寻找峰值(滑动窗口)—day39