算法思想
滑动窗⼝算法的思路是这样:
-
我们在字符串 S 中使⽤双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为⼀个「窗⼝」。
-
我们先不断地增加 right 指针扩⼤窗⼝ [left, right],直到窗⼝中的字符串符合要求(包含了 T 中的所有字符)。
-
此时,我们停⽌增加 right,转⽽不断增加 left 指针缩⼩窗⼝ [left, right],直到窗⼝中的字符串不再符合要求(不包含 T 中的所有字符了)。 同时,每次增加 left,我们都要更新⼀轮结果。
-
重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。
int left = 0, right = 0;
while (right < s.size()) {
window.add(s[right]);
right++;
while (valid) {
window.remove(s[left]);
left++;
}
}
1.最小覆盖子串
class Solution {
public String minWindow(String s, String t) {
//1. 用一个map来记录需要多少字符,每个字符需要多少次
Map<Character , Integer> need = new HashMap<>();
for(char c : t.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);
//2. 用一个窗口来表示是否满足条件
Map<Character , Integer> win = new HashMap<>();
int l = 0 , r = 0 , count = 0;
int len = Integer.MAX_VALUE , start = 0 , end = 0; //记录长度和起始终止位置
while(r < s.length()){
char a = s.charAt(r);
if(need.containsKey(a)){
win.put(a , win.getOrDefault(a , 0 ) + 1);
if(win.get(a).intValue() == need.get(a).intValue()) count++;
}
r++;
while(count == need.size()){ //当满足条件的时候
if(len > r-l){
len = r - l;
start = l;
end = r;
}
char b = s.charAt(l);
if(need.containsKey(b)){
win.put(b , win.getOrDefault(b,0)-1);
if(win.get(b) < need.get(b)) count--;
}
l++;
}
}
return s.substring(start , end);
}
}
2.无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
if(n == 0 || n == 1) return n;
Map<Character , Integer> map = new HashMap<>();
int res = 0;
for(int i = 0 , j = 0 ; i < n ; i++){
char c = s.charAt(i);
if(map.containsKey(c)){
j = Math.max(j , map.get(c));
}
map.put(c , i+1);
res = Math.max(res , i+1 - j);
}
return res;
}
}
3.找到字符串中所有字母异位词
(leetcode-cn.com/problems/fi…)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
Map<Character,Integer> need = new HashMap<>();
for(char c : p.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);
Map<Character,Integer> win = new HashMap<>();
int count = 0 , l = 0 , r = 0;
while(r < s.length()){
char a = s.charAt(r);
if(need.containsKey(a)){
win.put(a , win.getOrDefault(a , 0)+1);
if(win.get(a).intValue() == need.get(a).intValue()) count++;
}
r++;
while(count == need.size()){
if(r - l == p.length()){ //如果满足条件,加入到res中
res.add(l);
}
char b = s.charAt(l);
if(need.containsKey(b)){
win.put(b , win.getOrDefault(b ,0)-1);
if(win.get(b) < need.get(b)) count--;
}
l++;
}
}
return res;
}
}
4.判断字符串是否包含排列
class Solution {
public boolean checkInclusion(String s, String p) {
Map<Character , Integer> need = new HashMap<>();
for(char c : s.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);
Map<Character,Integer> win = new HashMap<>();
int l = 0 , r = 0;
int count = 0;
while(r < p.length()){
char a = p.charAt(r);
if(need.containsKey(a)){
win.put(a , win.getOrDefault(a , 0) + 1);
if(win.get(a).intValue() == need.get(a).intValue()) count++;
}
r++;
while(r - l >= s.length()){
if(count == need.size()) return true;
char b = p.charAt(l);
if(need.containsKey(b)){
if(win.get(b) == need.get(b)) count--;
win.put(b , win.getOrDefault(b,0)-1);
}
l++;
}
}
return false;
}
}
固定大小的窗口问题
1.滑动窗口最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if(n < 2) return nums;
int[] res = new int[n-k+1];
//双向链表,队列中数组位置的数按从大到小排序
LinkedList<Integer> list = new LinkedList<>();
for(int i = 0 ; i < n ; i++){
//// 保证从大到小 如果前面数小 弹出
while(!list.isEmpty() && nums[list.peekLast()] <= nums[i]) list.pollLast();
list.addLast(i);
// 初始化窗口 等到窗口长度为k时 下次移动在删除过期数值
if(list.peek() <= i-k) list.poll();
if(i - k + 1 >= 0) res[i-k+1] = nums[list.peek()];
}
return res;
}
}