滑动窗口算法一般需要用到双指针来进行解决,另外一类需要用到特殊的数据结构如Map(计数),队列(大小顺序记录)等。核心在于如何控制窗口的移动,左右指针什么时候动,什么时候不动,这个是关键。
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路:
可抽象为伪代码
List s;
List<Integer> window;
Integer left = 0, right = 0;
while (right<length){
window.add(s[right]);
right++;
while (window 符合要求){
//更新结果
update(window);
//移动left指针
window.remove(s[left]);
left++;
}
}
通过以上可知 窗口本质就是个数组,重点在定义什么样的窗口符合要求,和如何去更新结果。
根据题意,我们可以定义两个Map分别计数符合要求的窗口和目前窗口的计数,更新的结果即为最小符合做的字符串是什么。
public String minWindow(String s, String t) {
int left = 0, right = 0;
Integer matchSize = 0;
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c, need.get(c) != null ? need.get(c) + 1 : 1);
}
int minLength = Integer.MAX_VALUE;
int start = 0;
while (right < s.length()) {
char c1 = s.charAt(right);
window.put(c1, window.get(c1) != null ? window.get(c1) + 1 : 1);
if (need.get(c1) != null) {
if (window.get(c1).equals(need.get(c1))) {
matchSize++;
}
}
right++;
while (matchSize == need.size()) {
if (right - left < minLength) {
start = left;
minLength = right - left;
}
// 重新计算窗口值
char c2 = s.charAt(left);
if (need.get(c2) != null) {
window.put(c2, window.get(c2)- 1);
if (window.get(c2) < need.get(c2)) {
matchSize--;
}
}
left++;
}
}
return minLength == Integer.MAX_VALUE ? "" : s.substring(start, start+minLength);
}
和上题一样,就是在符合条件的时候,更新结果的时候,判断条件改为了right-left=字串长度。
public List<Integer> findAnagrams(String s, String p) {
int left = 0, right = 0;
List<Integer> res = new ArrayList<>();
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
for (char c : p.toCharArray()) {
need.put(c, need.get(c) != null ? need.get(c) + 1 : 1);
}
Integer match = 0;
while (right < s.length()) {
char c1 = s.charAt(right);
window.put(c1, window.get(c1) != null ? window.get(c1) + 1 : 1);
if (window.get(c1).equals(need.get(c1))) {
match++;
}
right++;
while (match == need.size()) {
if (right - left == p.length()) {
res.add(left);
}
char c2 = s.charAt(left);
window.put(c2, window.get(c2) - 1);
if (need.get(c2) != null) {
if (window.get(c2) < need.get(c2)) {
match--;
}
}
left++;
}
}
return res;
}
public int lengthOfLongestSubstring(String s) {
int left=0,right=0;
Map<Character,Integer> window=new HashMap<>();
int max=Integer.MIN_VALUE;
while (right<s.length()){
char c1=s.charAt(right);
window.put(c1,window.get(c1)!=null?window.get(c1)+1:1);
right++;
while (window.get(c1)>1){
char c2=s.charAt(left);
window.put(c2,window.get(c2)-1);
left++;
}
if (right-left>max){
max=right-left;
}
}
return max==Integer.MIN_VALUE?0:max;
}
LinkedList<Integer> linkedList = new LinkedList<>();
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] res = new int[n - k + 1];
int left = 0;
int right = 0;
int count = 0;
while (right < n) {
clean_queue(nums,right,k);
linkedList.add(right);
right++;
while (right-left >= k && right <= n) {
res[count++] = nums[linkedList.getFirst()];
left++;
}
}
return res;
}
public void clean_queue(int[] nums, int i, int k) {
if (!linkedList.isEmpty() && i - k == linkedList.getFirst()) {
linkedList.removeFirst();
}
while (!linkedList.isEmpty() && nums[i] > nums[linkedList.getLast()]) {
linkedList.removeLast();
}
}```
通过总结,可抽象出模版
```java
int left = 0, right = 0;
while (right < s.size()) {
//添加窗口
window.add(s[right]);
right++;
while (符合窗口规则) {
//更新值
window.remove(s[left]);
left++;
}
}