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之滑动窗口算法小结