LeetCode Hot 100
3. 无重复字符的最长子串
难度:中等 | LeetCode 链接
给定一个字符串 s,请找出其中不含有重复字符的最长子串的长度。
1 2 3
| 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
|
思路
使用滑动窗口维护一个不含重复字符的区间 [left, right]。当右指针遇到重复字符时,移动左指针直到窗口内无重复。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { public int lengthOfLongestSubstring(String s) { Set<Character> set = new HashSet<>(); int length = s.length(); int lPointer = 0; int ans = 0; for(int rPointer = 0;rPointer<length;rPointer++) { Character c = s.charAt(rPointer); while(set.contains(c)){ set.remove(s.charAt(lPointer)); lPointer++; } set.add(c); ans = Math.max(ans,rPointer-lPointer+1); } return ans; } }
|
复杂度:时间 O(n),空间 O (min (m, n)) 其中 m 是字符集大小。比如英文字母最多 26 个,ASCII 字符最多 128 个
438. 找到字符串中所有字母异位词
难度:中等 | LeetCode 链接
给定两个字符串 s 和 p,找到 s 中所有 p 的异位词的子串,返回这些子串的起始索引。
1 2 3 4 5
| 输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba",它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac",它是 "abc" 的异位词。
|
思路
把 p 映射为频次数组, 维护固定长度为 p.length() 的滑动窗口,在 s 上移动,统计窗口内字符频次与 p 的频次进行比较。
代码
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
| class Solution { public List<Integer> findAnagrams(String s, String p) { int[] pCnt = new int[26]; for(int i = 0;i<p.length();i++){ pCnt[p.charAt(i) - 'a'] ++ ; } int[] wCnt = new int[26]; List<Integer> res = new ArrayList<>(); for(int r = 0;r<s.length();r++){ int l = r - p.length() + 1; wCnt[s.charAt(r) - 'a'] ++; if(r<p.length()-1){ continue; } if(Arrays.equals(pCnt,wCnt)){ res.add(l); } wCnt[s.charAt(l) - 'a'] --; } return res; } }
|
复杂度:时间 O(n),空间 O(1)
239. 滑动窗口最大值
难度:困难 | LeetCode 链接
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。返回每个窗口中的最大值。
1 2
| 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7]
|
思路
使用单调队列(递减)。队头始终是当前窗口最大值。窗口移动时,移除过期元素并保持队列单调性。
代码
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
| class Solution { public int[] maxSlidingWindow(int[] nums, int k) { Deque<Integer> q = new ArrayDeque<>(); int[] ans = new int[nums.length-k+1]; for(int i =0;i<nums.length;i++){ while(!q.isEmpty()&&nums[q.getLast()]<=nums[i]){ q.removeLast(); } q.addLast(i); if(q.getFirst()<i-k+1){ q.removeFirst(); } if(i>=k-1){ ans[i-k+1] = nums[q.getFirst()]; } } return ans; } }
|
复杂度:时间 O(n),空间 O(k)
76. 最小覆盖子串
难度:困难 | LeetCode 链接
给你一个字符串 s 和一个字符串 t,返回 s 中涵盖 t 所有字符的最小子串。
1 2
| 输入:s = "ADOBECODEBANC", t = "ABC" 输出:"BANC"
|
思路
滑动窗口统计 t 中字符需求。右指针扩展满足需求后,左指针尽量收缩以缩小窗口,并记录最短区间。
代码
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 50 51
| class Solution { public String minWindow(String s, String t) { int[] tCharArr = new int[128];
for(int i=0;i<t.length();i++){ char c = t.charAt(i); tCharArr[c]++; } int[] ansCharArr = new int[128]; int l = 0; int ansL = -1; int ansR = s.length(); for(int r=0;r<s.length();r++){ char c = s.charAt(r); ansCharArr[c]++; while(isCovered(tCharArr,ansCharArr)){ if(r-l<ansR-ansL){ ansL = l; ansR = r; } ansCharArr[s.charAt(l)]--; l++; } } return ansL < 0 ? "":s.substring(ansL,ansR+1); }
private boolean isCovered(int[] tCharArr,int[] ansCharArr){ for(int i='A';i<='Z';i++){ if(ansCharArr[i]<tCharArr[i]){ return false; } } for(int i='a';i<='z';i++){ if(ansCharArr[i]<tCharArr[i]){ return false; } } return true; } }
|
复杂度:时间 O(n),空间 O(1)