我的滑动窗口——在困难的力扣题上提交提交
1 概要
本文将梳理出 LeetCode 上关于滑动窗口类型的题目,前期笔者可能忙于刷题,暂时只能通过的代码贴在这里,后序有时间慢慢补充分析和思路。喜欢的可以点赞、关注一波~
2 实战
2.1 固定滑动窗口
2.1.1 【1456.定长子串中元音的最大数目】
class Solution {
public int maxVowels(String s, int k) {
if (s == null || s.length() == 0) {
throw new IllegalArgumentException();
}
char[] chs = s.toCharArray();
int length = chs.length;
int cntVowel = 0;
for (int i = 0; i < k; i++) {
cntVowel += getVowelCnt(chs[i]);
}
int res = cntVowel;
for (int i = k; i < length; i++) {
cntVowel += getVowelCnt(chs[i]) - getVowelCnt(chs[i - k]);
res = Math.max(res, cntVowel);
}
return res;
}
private int getVowelCnt(char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ? 1 : 0;
}
}
2.1.2 【1423. 可获得的最大点数】
class Solution {
public int maxScore(int[] cardPoints, int k) {
// 由于是从头或者尾抽取 k 张牌,所以剩下的 len - k 张牌一定是连续的。求抽取的最大,那就是 len - k 剩下的最小
int length = cardPoints.length;
int windowSize = length - k;
// 形成 window
int windowSum = 0;
int sumAll = 0;
for (int i = 0; i < windowSize; i++) {
sumAll += cardPoints[i];
windowSum += cardPoints[i];
}
int windowMinSum = windowSum;
for (int i = windowSize; i < length; i++) {
sumAll += cardPoints[i];
windowSum += cardPoints[i] - cardPoints[i - windowSize];
windowMinSum = Math.min(windowMinSum, windowSum);
}
return sumAll - windowMinSum;
}
}
2.1.3 【1658. 将 x 减到 0 的最小操作】
class Solution {
public int minOperations(int[] nums, int x) {
// 思路同取牌那道题目,转换为求窗口和为 sum - x 的最大窗口
if (nums == null || nums.length == 0) {
throw new IllegalArgumentException();
}
int length = nums.length;
int target = Arrays.stream(nums).sum() - x;
int left = 0, right = 0;
int windowSize = -1;
while (right < length) {
target -= nums[right++];
while (left < right && target < 0) {
target += nums[left++];
}
if (target == 0) {
windowSize = Math.max(windowSize, right - left);
}
}
return windowSize == -1 ? -1 : length - windowSize;
}
}
2.1.4【1052. 爱生气的书店老板】
class Solution {
public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
// 求某个时间段内因为老板生气而被气走的人最多,求出被气走最多的人数
int length = customers.length;
int left = 0, right = 0;
int angryCustomers = 0;
int goodCustomers = 0;
for (int i = 0; i < minutes; i++) {
if (grumpy[i] == 0) {
goodCustomers += customers[i];
} else {
angryCustomers += customers[i];
}
}
int diff = angryCustomers;
for (int i = minutes; i < length; i++) {
if (grumpy[i] == 0) {
goodCustomers += customers[i];
} else {
angryCustomers += customers[i];
}
// 要减去不在窗口内的顾客
angryCustomers -= customers[i - minutes] * grumpy[i - minutes];
diff = Math.max(diff, angryCustomers);
}
return goodCustomers + diff;
}
}
2.1 Minimum Window Substring
题目链接:76. Minimum Window Substring
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<>(), window = new HashMap<>();
// 统计 t 中字符的数量
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
// 最短子串的开始位置,长度,s 字符串的长度
int start = 0, lengthWindow = Integer.MAX_VALUE, length = s.length();
int valid = 0, target = need.size();
char[] chs = s.toCharArray();
for (int left = 0, right = 0; right < length; right++) {
char c = chs[right];
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (Objects.equals(window.get(c), need.get(c))) {
valid++;
}
}
while (valid == target) {
if (right - left + 1 < lengthWindow) {
start = left;
lengthWindow = right - left + 1;
}
// 缩小窗口
char d = chs[left++];
if (need.containsKey(d)) {
if (Objects.equals(window.get(d), need.get(d))) {
valid--;
}
window.put(d, window.get(d) - 1);
}
}
}
return lengthWindow == Integer.MAX_VALUE ? "" : s.substring(start, start + lengthWindow);
}
}
2.2 Find All Anagrams in a String
题目链接:438. Find All Anagrams in a String
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if (s == null || p == null || s.length() == 0 || p.length() == 0) {
throw new IllegalArgumentException();
}
List<Integer> res = new ArrayList<>();
int lens = s.length(), lenp = p.length();
// 如果 s 的长度小于 p 的长度
if (lens < lenp) return res;
Map<Character, Integer> need = new HashMap<>(), window = new HashMap<>();
for (char c : p.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int valid = 0, target = need.size();
char[] chs = s.toCharArray();
for (int left = 0, right = 0; right < lens; right++) {
char c = chs[right];
window.put(c, window.getOrDefault(c, 0) + 1);
// 当窗口内包含所有 p 中的字符时
while (window.get(c) > need.getOrDefault(c, 0)) {
// left 需要移动到 right 的位置,且 window 也要做相应的变更
char d = chs[left++];
window.put(d, window.get(d) - 1);
}
// 判断一下窗口的宽度,如果与 p 的长度一致才记录 left
if (right - left + 1 == lenp) res.add(left);
}
return res;
}
}
2.3 Permutation in String
题目链接:567. Permutation in String
class Solution {
public boolean checkInclusion(String s1, String s2) {
if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) {
throw new IllegalArgumentException();
}
Map<Character, Integer> need = new HashMap<>(), window = new HashMap<>();
for (char c : s1.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int len1 = s1.length(), len2 = s2.length();
int left = 0, right = 0;
while (right < len2) {
char c = s2.charAt(right++);
window.put(c, window.getOrDefault(c, 0) + 1);
while (window.get(c) > need.getOrDefault(c, 0)) {
char d = s2.charAt(left++);
window.put(d, window.get(d) - 1);
}
if (right - left == len1) return true;
}
return false;
}
}
2.4 Longest Substring Without Repeating Characters
题目链接:3. Longest Substring Without Repeating Characters
class Solution {
public int lengthOfLongestSubstring(String s) {
// 滑动窗口:左右指针,右指针一直向右边滑动,右指针每到一个新的位置该位置对应字符的统计计数加1,若计数大于1,则窗口向左收缩直至计数值恢复1
if (s == null) throw new IllegalArgumentException();
int length = s.length();
if (length == 0) return 0;
Map<Character, Integer> window = new HashMap<>();
int left = 0, right = 0, res = 0;
while (right < length) {
char c = s.charAt(right++);
window.put(c, window.getOrDefault(c, 0) + 1);
// 如果 right 指向的字符大于1次
while (window.get(c) > 1) {
// 左指针开始向右移动,并更新 window 中的字符数量
char d = s.charAt(left++);
window.put(d, window.get(d) - 1);
}
res = Math.max(res, right - left);
}
return res;
}
}