前言滑动窗口主要解决“满足某个条件的连续子串”问题,因为我们枚举 区间、子数组、子串 问题的时候时间复杂度是O ( n 2 ) O(n^2) O ( n 2 ) ,使用滑窗可以将时间复杂度优化至O ( n ) O(n) O ( n ) 。
代码模板如下:
1 2 3 4 for (int l = 0 , r = 0 ; r < n ; r++) { while (l <= r && check ()) l++; }
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 示例 1 : 输入 : s = “abcabcbb” 输出 : 3 解释 : 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2 : 输入 : s = “bbbbb” 输出 : 1 解释 : 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示例 3 : 输入 : s = “pwwkew” 输出 : 3 解释 : 因为无重复字符的最长子串是 “wke”,所以其长度为 3。 请注意,你的答案必须是子串的长度,“pwke” 是一个子序列,不是子串。 提示: 0 <= s.length <= 5 * 104 s 由英文字母、数字、符号和空格组成.
题目解析
题目旨在找到最长的无重复的子串,所以我们在滑窗的过程中,只要保证窗口中元素一直是处于不重复的情况即可。换句话说,只要右指针的元素已经出现了重复,我们就滑动左指针。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int lengthOfLongestSubstring (String s) { int [] occ = new int [128 ]; int res = 0 ; for (int l = 0 , r = 0 ; r < s.length(); r++) { while (l <= r && occ[s.charAt(r)] == 1 ) occ[s.charAt(l++)] = 0 ; occ[s.charAt(r)] = 1 ; res = Math.max(res, r - l + 1 ); } return res; } }
DNA序列 由一系列核苷酸组成,缩写为 ‘A’, ‘C’, ‘G’ 和 ‘T’.。
例如,“ACGAATTCCG” 是一个 DNA序列 。 在研究 DNA 时,识别 DNA 中的重复序列非常有用。
给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。
1 2 3 4 5 6 7 8 示例 1 : 输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT” 输出:[“AAAAACCCCC”,“CCCCCAAAAA”] 示例 2 : 输入:s = “AAAAAAAAAAAAA” 输出:[“AAAAAAAAAA”] 提示:0 <= s.length <= 105 s[i]==‘A’、‘C’、‘G’ or ‘T’
题目解析
此题比较简单,因为已经固定了窗口大小为10,因此我们只需要用一个指针即可,因为左指针一旦确定,右指针必然位于后10位。 因此,固定窗口的滑窗其实可以直接一层loop就可以了。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public List findRepeatedDnaSequences (String s) { Set<String> occ = new HashSet <>(); Set<String> res = new HashSet <>(); for (int i = 0 ; i < s.length() - 9 ; i++) { String sub = s.substring(i, i + 10 ); if (occ.contains(sub)) res.add(sub); occ.add(sub); } List<String> ans = new LinkedList <>(); ans.addAll(res); return ans; } }
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果 s 中存在这样的子串,我们保证它是唯一的答案。
1 2 3 4 5 6 7 8 9 10 11 12 示例 1 : 输入:s = “ADOBECODEBANC”, t = “ABC” 输出:“BANC” 示例 2 : 输入:s = “a”, t = “a” 输出:“a” 示例 3 : 输入: s = “a”, t = “aa” 输出: “” 解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。 提示:1 <= s.length, t.length <= 105 , s 和 t 由英文字母组成
题目解析
判断某个子串是否包含字符串t,只需要判断是否包含t中所有的字符(数量也要满足大于等于的关系)即可。而且题目中也说明了,只有英文字母,大写字母”A”的ASCII码是65,小写字母’z’的ASCII码是122,因此他们之间一共有58个字符,所以我们只需要开一个长度有58的数组来表示每个字符的出现次数即可。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Solution { int [] win = new int [58 ]; int [] tcnter = new int [58 ]; boolean check () { for (int i = 0 ; i < 58 ; i++) { if (tcnter[i] > win[i]) { return false ; } } return true ; } public String minWindow (String s, String t) { for (char c : t.toCharArray()) { tcnter[c - 'A' ]++; } char [] cs = s.toCharArray(); int minlen = 100010 , st = -1 ; int l = 0 , r = 0 ; for (; r < cs.length; r++) { win[cs[r] - 'A' ]++; while (l <= r && check()) { if (r - l + 1 < minlen) { minlen = r - l + 1 ; st = l; } win[cs[l++] - 'A' ]--; } } return st == -1 ? "" : s.substring(st, st + minlen); } }
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [ n u m s l , n u m s l + 1 , … , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, …, nums_{r-1}, nums_r] [ n u m s l , n u m s l + 1 , … , n u m s r − 1 , n u m s r ] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 示例 1 : 输入:target = 7 , nums = [2 ,3 ,1 ,2 ,4 ,3 ] 输出:2 解释:子数组 [4 ,3 ] 是该条件下的长度最小的子数组。 示例 2 : 输入:target = 4 , nums = [1 ,4 ,4 ] 输出:1 示例 3 : 输入:target = 11 , nums = [1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ] 输出:0 提示:1 <= target <= 10 ^9 , 1 <= nums.length <= 10 ^5 , 1 <= nums[i] <= 10 ^5
题目解析
要求子数组的和要满足大于等于target,且要最小,因此我们只需要在当前窗口内的元素满足大于等于target的时候,我们就尝试去缩小区间,试试能够找出更小的区间。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int minSubArrayLen (int target, int [] nums) { int res = Integer.MAX_VALUE; for (int l = 0 , r = 0 , sum = 0 ; r < nums.length; r++) { sum += nums[r]; while (l <= r && sum >= target) { res = Math.min(res, r - l + 1 ); sum -= nums[l++]; } } return res == Integer.MAX_VALUE ? 0 : res; } }
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
1 2 3 4 5 6 7 8 9 10 11 示例 1 : 输入:s1 = “ab” s2 = “eidbaooo” 输出:true 解释:s2 包含 s1 的排列之一 (“ba”). 示例 2 : 输入:s1 = “ab” s2 = “eidboaoo” 输出:false 提示:1 <= s1 .length, s2 .length <= 10 ^4 , s1 和 s2 仅包含小写字母
题目解析
此题依然是固定大小的滑动窗口,我们只需要用一个与s1等长的窗口进行滑动即可。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Solution { int [] s1_cnter = new int [26 ]; int [] s2_cnter = new int [26 ]; boolean check () { for (int i = 0 ; i < 26 ; i++) { if (s1_cnter[i] < s2_cnter[i]) { return false ; } } return true ; } public boolean checkInclusion (String s1, String s2) { if (s1.length() > s2.length()) { return false ; } for (int i = 0 ; i < s1.length(); i++) { s1_cnter[s1.charAt(i) - 'a' ]++; s2_cnter[s2.charAt(i) - 'a' ]++; } if (check()) { return true ; } for (int l = 0 , r = s1.length(); r < s2.length(); r++, l++) { s2_cnter[s2.charAt(r) - 'a' ]++; s2_cnter[s2.charAt(l) - 'a' ]--; if (check()) { return true ; } } return false ; } }
给你两个长度相同的字符串,s 和 t。
将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。
如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 示例 1 : 输入:s = “abcd”, t = “bcdf”, maxCost = 3 输出:3 解释:s 中的 “abc” 可以变为 “bcd”。开销为 3 ,所以最大长度为 3 。 示例 2 : 输入:s = “abcd”, t = “cdef”, maxCost = 3 输出:1 解释:s 中的任一字符要想变成 t 中对应的字符,其开销都是 2 。因此,最大长度为 1 。 示例 3 : 输入:s = “abcd”, t = “acde”, maxCost = 0 输出:1 解释:a -> a, cost = 0 ,字符串未发生变化,所以最大长度为 1 。 提示:1 <= s.length, t.length <= 10 ^5 , 0 <= maxCost <= 10 ^6 , s 和 t 都只含小写英文字母。
题目解析
此题的目的在于找到一个 对应位置 的子字符串,使得每个位置一一进行转换后,开销不会超过maxcost,求最长的长度。我们只需要保证窗口内的开销是小于等于maxcost即可,如果不满足则滑动左指针。
代码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int equalSubstring (String s, String t, int maxCost) { int res = 0 ; for (int l = 0 , r = 0 , sum = 0 ; r < s.length(); r++) { sum += Math.abs(s.charAt(r) - t.charAt(r)); while (l <= r && sum > maxCost) { sum -= Math.abs(s.charAt(l) - t.charAt(l++)); } res = Math.max(res, r - l + 1 ); } return res; } }