灵神力扣题单之滑动窗口

117 阅读45分钟

1 介绍

本博客用来记录灵神力扣题单之滑动窗口

2 训练

2.1 定长滑动窗口

题目11456. 定长子串中元音的最大数目

解题思路:维护一个长度为k的窗口,注意移除的元素s[i-1]和新加的元素s[i+k-1]

C++代码如下,

class Solution {
public:
    int maxVowels(string s, int k) {
        int curc = 0;
        int n = s.size();
        string t = "aeiou";
        for (int i = 0; i < k && i < n; ++i) {
            if (t.find(s[i]) != string::npos) {
                curc += 1;
            }
        }
        int maxc = curc;
        for (int i = 1; i < n-k+1; ++i) {
            if (t.find(s[i-1]) != string::npos) {
                curc -= 1;
            }
            if (t.find(s[i+k-1]) != string::npos) {
                curc += 1;
            }
            maxc = max(maxc, curc);
        }
        return maxc;
    }
};

python3代码如下,

class Solution:
    def maxVowels(self, s: str, k: int) -> int:
        n = len(s)
        cur = s[0:k]
        curc = 0
        for c in cur:
            if c in "aeiou":
                curc += 1
        maxc = curc 
        for i in range(1,n-k+1):
            if s[i-1] in "aeiou":
                curc -= 1

            if s[i+k-1] in "aeiou":
                curc += 1

            maxc = max(maxc, curc)
        return maxc 

题目22269. 找到一个数字的 K 美丽值

解题思路:模拟。

C++代码如下,

class Solution {
public:
    int divisorSubstrings(int num, int k) {
        string s = to_string(num);
        int res = 0;
        int n = s.size();
        for (int i = 0; i < n-k+1; ++i) {
            string t = s.substr(i,k);
            int a = stoi(t);
            if (a != 0 && num % a == 0) {
                res += 1;
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def divisorSubstrings(self, num: int, k: int) -> int:
        s = str(num)
        n = len(s)
        res = 0
        for i in range(0,n-k+1):
            t = s[i:i+k]
            t = int(t)
            if t != 0 and num % t == 0:
                res += 1
        return res 

题目31984. 学生分数的最小差值

解题思路如下:模拟。

C++代码如下,

class Solution {
public:
    int minimumDifference(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int res = int(1e9);
        for (int i = 0; i < n-k+1; ++i) {
            int a = nums[i];
            int b = nums[i+k-1];
            res = min(res, b-a);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def minimumDifference(self, nums: List[int], k: int) -> int:
        nums.sort()
        n = len(nums)
        res = int(1e20)
        for i in range(n-k+1):
            a = nums[i]
            b = nums[i+k-1]
            res = min(res, b-a)
        return res 

题目4643. 子数组最大平均数 I

解题思路如下:模拟即可。

C++代码如下,

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        int n = nums.size();
        double cur = 0;
        for (int i = 0; i < k; ++i) cur += nums[i];
        double res = cur;
        for (int i = 1; i < n-k+1; ++i) {
            cur -= nums[i-1];
            cur += nums[i+k-1];
            res = max(res, cur);
        }
        res /= k;
        return res;
    }
};

python3代码如下,

class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -> float:
        n = len(nums)
        cur = 0
        for i in range(0,k):
            cur += nums[i]
        res = cur 
        for i in range(1,n-k+1):
            cur -= nums[i-1]
            cur += nums[i+k-1]
            res = max(res, cur)
        res /= k 
        return res 

题目51343. 大小为 K 且平均值大于等于阈值的子数组数目

解题思路:模拟。

C++代码如下,

class Solution {
public:
    int numOfSubarrays(vector<int>& arr, int k, int threshold) {
        int n = arr.size();
        int cur = 0;
        for (int i = 0; i < k; ++i) cur += arr[i];
        int res = 0;
        if (cur >= threshold * k) res += 1;
        for (int i = 1; i < n-k+1; ++i) {
            cur -= arr[i-1];
            cur += arr[i+k-1];
            if (cur >= threshold * k) res += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:
        res = 0
        cur = 0
        n = len(arr)
        for i in range(k):
            cur += arr[i]
        if cur >= threshold * k:
            res += 1
        for i in range(1,n-k+1):
            cur -= arr[i-1]
            cur += arr[i+k-1]
            if cur >= threshold * k:
                res += 1
        return res 

题目62090. 半径为 k 的子数组平均值

解题思路:模拟。

C++代码如下,

class Solution {
public:
    vector<int> getAverages(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> res(n, -1);
        if (2 * k + 1 > n) return res;
        long long cur = 0;
        for (int i = 0; i < 2 * k + 1; ++i) cur += nums[i];
        res[k] = cur / (2 * k + 1);
        for (int i = k+1; i < n-k; ++i) {
            cur -= nums[i-k-1];
            cur += nums[i+k];
            res[i] = cur / (2 * k + 1);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def getAverages(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        res = [-1] * n 
        if 2 * k + 1 > n: #特判
            return res 
        cur = 0
        for i in range(2 * k + 1):
            cur += nums[i]
        res[k] = cur // (2 * k + 1)
        for i in range(k+1,n-k):
            cur -= nums[i-k-1]
            cur += nums[i+k]
            res[i] = cur // (2 * k + 1)
        return res 

题目72379. 得到 K 个黑块的最少涂色次数

解题思路:模拟。

C++代码如下,

class Solution {
public:
    int minimumRecolors(string blocks, int k) {
        int n = blocks.size();
        int cur = 0;
        for (int i = 0; i < k; ++i) {
            if (blocks[i] == 'W') {
                cur += 1;
            }
        }
        int res = cur;
        for (int i = 1; i < n-k+1; ++i) {
            if (blocks[i-1] == 'W') cur -= 1;
            if (blocks[i+k-1] == 'W') cur += 1;
            res = min(res, cur);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def minimumRecolors(self, blocks: str, k: int) -> int:
        cur = 0
        n = len(blocks)
        for i in range(k):
            if blocks[i] == 'W':
                cur += 1
        res = cur 
        for i in range(1,n-k+1):
            if blocks[i-1] == "W":
                cur -= 1
            if blocks[i+k-1] == "W":
                cur += 1
            res = min(res, cur)
        return res 

题目81052. 爱生气的书店老板

解题思路:模拟。

C++代码如下,

class Solution {
public:
    int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int k) {
        int n = customers.size();
        int cur = 0;
        for (int i = 0; i < k; ++i) {
            if (grumpy[i] == 1) {
                cur += customers[i];
            }
        }
        int res = cur;
        for (int i = 1; i < n-k+1; ++i) {
            if (grumpy[i-1] == 1) {
                cur -= customers[i-1];
            }
            if (grumpy[i+k-1] == 1) {
                cur += customers[i+k-1];
            }
            res = max(res, cur);
        }
        for (int i = 0; i < n; ++i) {
            if (grumpy[i] == 0) {
                res += customers[i];
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxSatisfied(self, customers: List[int], grumpy: List[int], minutes: int) -> int:
        n = len(customers)
        cur = 0
        for i in range(minutes):
            if grumpy[i] == 1:
                cur += customers[i]
        res = cur 
        for i in range(1,n-minutes+1):
            if grumpy[i-1] == 1:
                cur -= customers[i-1]
            if grumpy[i+minutes-1] == 1:
                cur += customers[i+minutes-1]
            res = max(res, cur)
        for i in range(n):
            if grumpy[i] == 0:
                res += customers[i]
        return res 

题目92841. 几乎唯一子数组的最大和

解题思路:模拟。注意滑动区间内次数由1变0的情况,以及次数由0变1的情况。

C++代码如下,

class Solution {
public:
    long long maxSum(vector<int>& nums, int m, int k) {
        int n = nums.size();
        unordered_map<int, int> map_val_cnt;
        long long s = 0;
        for (int i = 0; i < k; ++i) {
            map_val_cnt[nums[i]] += 1;
            s += nums[i];
        }
        long long res = 0;
        int a = 0; //滑动区间内不同数字的个数
        for (auto [val, cnt] : map_val_cnt) {
            if (cnt > 0) a += 1;
        }
        if (a >= m) res = s;
        for (int i = 1; i < n-k+1; ++i) {
            s -= nums[i-1];
            s += nums[i+k-1];
            map_val_cnt[nums[i-1]] -= 1;
            if (map_val_cnt[nums[i-1]] == 0) a -= 1;            
            map_val_cnt[nums[i+k-1]] += 1;
            if (map_val_cnt[nums[i+k-1]] == 1) a += 1;
            if (a >= m) {
                res = max(res, s);
            } 
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxSum(self, nums: List[int], m: int, k: int) -> int:
        map_val_cnt = collections.defaultdict(int)
        n = len(nums)
        s = 0 
        res = 0
        for i in range(k):
            map_val_cnt[nums[i]] += 1
            s += nums[i]
        a = 0 #不同的数字个数
        for val in map_val_cnt:
            cnt = map_val_cnt[val] 
            if cnt > 0:
                a += 1
        if a >= m:
            res = s 
        for i in range(1,n-k+1):
            s -= nums[i-1]
            s += nums[i+k-1]
            map_val_cnt[nums[i-1]] -= 1
            if map_val_cnt[nums[i-1]] == 0:
                a -= 1
            map_val_cnt[nums[i+k-1]] += 1
            if map_val_cnt[nums[i+k-1]] == 1:
                a += 1
            if a >= m:
                res = max(res, s)
        return res 

题目102461. 长度为 K 子数组中的最大和

解题思路:模拟。

C++代码如下,

class Solution {
public:
    long long maximumSubarraySum(vector<int>& nums, int k) {
        long long s = 0;
        int n = nums.size();
        unordered_map<int, int> map_val_cnt;
        for (int i = 0; i < k; ++i) {
            map_val_cnt[nums[i]] += 1;
            s += nums[i];
        }
        long long res = 0;
        int a = 0;
        for (auto [val, cnt] : map_val_cnt) {
            if (cnt > 0) {
                a += 1;
            }
        }
        if (a == k) res = s;
        for (int i = 1; i < n-k+1; ++i) {
            s -= nums[i-1];
            s += nums[i+k-1];
            map_val_cnt[nums[i-1]] -= 1;
            if (map_val_cnt[nums[i-1]] == 0) a -= 1;
            map_val_cnt[nums[i+k-1]] += 1;
            if (map_val_cnt[nums[i+k-1]] == 1) a += 1;
            if (a == k) {
                res = max(res, s);
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maximumSubarraySum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        map_val_cnt = collections.defaultdict(int)
        s = 0
        for i in range(k):
            s += nums[i]
            map_val_cnt[nums[i]] += 1
        a = 0 #不同数字的个数
        for val in map_val_cnt:
            cnt = map_val_cnt[val]
            if cnt > 0:
                a += 1
        res = 0
        if a == k:
            res = s 
        for i in range(1,n-k+1):
            s -= nums[i-1]
            s += nums[i+k-1]
            map_val_cnt[nums[i-1]] -= 1
            if map_val_cnt[nums[i-1]] == 0:
                a -= 1
            map_val_cnt[nums[i+k-1]] += 1
            if map_val_cnt[nums[i+k-1]] == 1:
                a += 1
            if a == k:
                res = max(res, s)
        return res 

题目111423. 可获得的最大点数

解题思路:模拟。

C++代码如下,

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        int n = cardPoints.size();
        vector<int> a = cardPoints;
        a.insert(a.end(), cardPoints.begin(), cardPoints.end());
        int s = 0;
        for (int i = n-k; i < n; ++i) {
            s += a[i];
        }
        int res = s;
        for (int i = n-k+1; i < n+1; ++i) {
            s -= a[i-1];
            s += a[i+k-1];
            res = max(res, s);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)
        cardPoints += cardPoints 
        s = 0
        for i in range(n-k,n):
            s += cardPoints[i]
        res = s 
        for i in range(n-k+1,n+1):
            s -= cardPoints[i-1]
            s += cardPoints[i+k-1]
            res = max(res, s)
        return res 

题目122134. 最少交换次数来组合所有的 1 II

解题思路:连续k个数中1的个数,答案等于k-res

C++代码如下,

class Solution {
public:
    int minSwaps(vector<int>& nums) {
        int n = nums.size();
        int k = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 1) {
                k += 1;
            }
        }
        vector<int> a = nums;
        a.insert(a.end(), nums.begin(), nums.end());
        int cur = 0;
        for (int i = 0; i < k; ++i) {
            if (a[i] == 1) {
                cur += 1;
            }
        }
        int res = cur;
        for (int i = 1; i < n; ++i) {
            if (a[i-1] == 1) cur -= 1;
            if (a[i+k-1] == 1) cur += 1;
            res = max(res, cur);
        }
        return k - res;
    }
};

python3代码如下,

class Solution:
    def minSwaps(self, nums: List[int]) -> int:
        n = len(nums)
        k = 0
        for i in range(n):
            if nums[i] == 1:
                k += 1
        nums += nums 
        cur = 0
        for i in range(k):
            if nums[i] == 1:
                cur += 1
        res = cur 
        for i in range(1,n):
            if nums[i-1] == 1:
                cur -= 1
            if nums[i+k-1] == 1:
                cur += 1
            res = max(res, cur)
        return k - res 

题目132653. 滑动子数组的美丽值

解题思路:固定区间长度k比较大,但nums[i]数值比较小,在[-50,50]之间。因此,可以使用cnt来统计第x小的数,这里数据结构可以选择数组,而非哈希表。

C++代码如下,

class Solution {
public:
    vector<int> getSubarrayBeauty(vector<int>& nums, int k, int x) {
        int n = nums.size();
        int cnt[101] = {0};
        for (int i = 0; i < k; ++i) {
            cnt[nums[i]+50] += 1;
        }
        vector<int> res(n-k+1, 0);
        //初始化
        int idx = x; //第x小数
        for (int j = 0; j < 50; ++j) {
            idx -= cnt[j];
            if (idx <= 0) {
                res[0] = j-50;
                break; 
            }
        }
        //剩余的数
        for (int i = 1; i < n-k+1; ++i) {
            int a = nums[i-1];
            int b = nums[i+k-1];
            cnt[a+50] -= 1;
            cnt[b+50] += 1;
            int idx = x; //第x小数
            for (int j = 0; j < 50; ++j) {
                idx -= cnt[j];
                if (idx <= 0) {
                    res[i] = j-50;
                    break;
                }
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]:
        n = len(nums)
        res = [0] * (n-k+1)
        cnt = [0] * 101 
        #初始化
        for i in range(k):
            cnt[nums[i]+50] += 1
        idx = x 
        for j in range(0,50):
            idx -= cnt[j]
            if idx <= 0:
                res[0] = j-50
                break #第x小的数
        #遍历剩余的数
        for i in range(1,n-k+1):
            cnt[nums[i-1]+50] -= 1
            cnt[nums[i+k-1]+50] += 1
            idx = x 
            for j in range(0,50):
                idx -= cnt[j]
                if idx <= 0:
                    res[i] = j-50 
                    break #第x小的数
        return res 

题目14567. 字符串的排列

解题思路:模拟。

C++代码如下,

class Solution {
public:
    bool check(int cnt1[], int cnt2[]) {
        for (int i = 0; i < 26; ++i) {
            if (cnt1[i] != cnt2[i]) return false;
        }
        return true;
    }

    bool checkInclusion(string s1, string s2) {
        int k = s1.size();
        int n = s2.size();
        if (k > n) { //特判
            return false;
        }
        //初始化
        int cnt1[26] = {0};
        int cnt2[26] = {0};
        for (int i = 0; i < k; ++i) {
            cnt1[s1[i]-'a'] += 1;
            cnt2[s2[i]-'a'] += 1;
        }
        if (check(cnt1, cnt2)) {
            return true;
        }
        //剩余的值
        for (int i = 1; i < n-k+1; ++i) {
            cnt2[s2[i-1]-'a'] -= 1;
            cnt2[s2[i+k-1]-'a'] += 1;
            if (check(cnt1,cnt2)) return true;
        }
        return false;
    }
};

python3代码如下,

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        k = len(s1)
        n = len(s2)
        if k > n: #特判
            return False
        cnt1 = [0] * 26 
        for c in s1:
            cnt1[ord(c)-ord('a')] += 1
        cnt2 = [0] * 26
        #初始化
        for i in range(k):
            cnt2[ord(s2[i])-ord('a')] += 1
        if cnt1 == cnt2:
            return True 
        #剩余的值
        for i in range(1,n-k+1):
            cnt2[ord(s2[i-1])-ord('a')] -= 1
            cnt2[ord(s2[i+k-1])-ord('a')] += 1
            if cnt2 == cnt1:
                return True 
        return False 

题目15438. 找到字符串中所有字母异位词

解题思路:小写字母,模拟。

C++代码如下,

class Solution {
public:
    bool check(int cnt1[], int cnt2[]) {
        for (int i = 0; i < 26; ++i) {
            if (cnt1[i] != cnt2[i]) return false;
        }
        return true;
    }

    vector<int> findAnagrams(string s, string p) {
        int n = s.size(), k = p.size();
        if (k > n) { //特判
            return {};
        }
        int cnt1[26] = {0};
        for (auto c : p) {
            cnt1[c-'a'] += 1;
        }
        //初始化
        int cnt2[26] = {0};
        for (int i = 0; i < k; ++i) {
            cnt2[s[i]-'a'] += 1;
        }
        vector<int> res; //答案
        if (check(cnt1, cnt2)) {
            res.emplace_back(0);
        }
        for (int i = 1; i < n-k+1; ++i) {
            cnt2[s[i-1]-'a'] -= 1;
            cnt2[s[i+k-1]-'a'] += 1;
            if (check(cnt1, cnt2)) {
                res.emplace_back(i);
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        k = len(p)
        n = len(s)
        if k > n: #特判
            return [] 
        cnt1 = [0] * 26
        for c in p:
            cnt1[ord(c)-ord('a')] += 1
        #初始化
        cnt2 = [0] * 26
        for i in range(k):
            cnt2[ord(s[i])-ord('a')] += 1
        res = []
        if cnt1 == cnt2:
            res.append(0)
        for i in range(1,n-k+1):
            cnt2[ord(s[i-1])-ord('a')] -= 1 
            cnt2[ord(s[i+k-1])-ord('a')] += 1
            if cnt1 == cnt2:
                res.append(i)
        return res 

题目162156. 查找给定哈希值的子串

解题思路:除法不好进行取余操作,尽量转换成加法、减法和乘法,迫不得已的情况下,才考虑乘法逆元。快速幂算法需牢记。减号取模,最好将结果值res = (res + mod) % mod,防止结果为负数。

C++代码如下,

class Solution {
public:
    int qmi(int a, int k, int mod) {
        long long res = 1;
        while (k > 0) {
            if (k & 1) res = res * a % mod;
            k >>= 1;
            a = (long long)a * a % mod;
        }
        return res;
    }

    string subStrHash(string s, int power, int mod, int k, int hashValue) {
        int n = s.size();
        long long cur = 0;
        for (int i = n-1; i > n-k-1; --i) {
            cur = (cur * power + (s[i] & 31)) % mod;
        }
        int res = 0;
        if (cur == hashValue) {
            res = n-k;
        }
        long long pk = qmi(power, k, mod);
        for (int i = n-k-1; i > -1; --i) {
            cur = (cur * power + (s[i] & 31) - pk * (s[i+k] & 31)) % mod;
            cur = (cur + mod) % mod; //上一步骤为减号,防止负数
            if (cur == hashValue) {
                res = i;
            }
        }
        return s.substr(res, k);
    }
};

python3代码如下,

class Solution:
    def subStrHash(self, s: str, power: int, mod: int, k: int, hashValue: int) -> str:
        n = len(s)
        cur = 0
        for i in range(n-1,n-k-1,-1):
            cur = (cur * power + (ord(s[i]) & 31)) % mod 
        res = 0
        if cur == hashValue:
            res = n-k
        pk = pow(power, k, mod)
        for i in range(n-k-1,-1,-1):
            cur = (cur * power + (ord(s[i]) & 31) - pk * (ord(s[i+k]) & 31)) % mod 
            if cur == hashValue:
                res = i 
        return s[res:res+k] 

题目172953. 统计完全子字符串

解题思路:首先依据相邻字符位置不超过2,将原字符串s切片成多个子串,对于每个子串进行如下处理。考虑只有一个字符出现k次,那么滑动区间长度为kcnt = collections.Counter()计数每个字符的出现次数,先初始化,然后考虑剩余的。

C++代码如下,

class Solution {
public:
    bool check(int cnt[], int k) {
        for (int i = 0; i < 26; ++i) {
            if (!(cnt[i] == 0 || cnt[i] == k)) {
                return false;
            }
        }
        return true;
    }

    int f(string s, int k) {
        int res = 0;
        int n = s.size();
        for (int m = 1; m < 27; ++m) {
            //滑动窗口长度m*k
            if (m*k > n) break;
            int cnt[26] = {0}; //数组能够解决的情况下,尽量不使用哈希表
            //初始化
            for (int i = 0; i < m*k; ++i) {
                cnt[s[i]-'a'] += 1;
            }
            if (check(cnt, k)) {
                res += 1;
            }
            //剩余的
            for (int i = 1; i < n-m*k+1; ++i) {
                cnt[s[i-1]-'a'] -= 1;
                cnt[s[i+m*k-1]-'a'] += 1;
                if (check(cnt, k)) {
                    res += 1;
                }
            }
        }
        return res;
    }

    int countCompleteSubstrings(string word, int k) {
        int i = 0;
        int n = word.size();
        int res = 0;
        while (i < n) {
            int j = i + 1;
            while (j < n && abs(word[j]-word[j-1]) <= 2) {
                j += 1;
            }
            res += f(word.substr(i,j-i), k);
            //cout << "i = " << i << ", j = " << j << ", word.substr(i,j-i) = " << word.substr(i,j-i) << ", k = " << k << ", f(word.substr(i,j-i), k) = " << f(word.substr(i,j-i), k) << endl;
            i = j; //更新i
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countCompleteSubstrings(self, word: str, k: int) -> int:
        def f(s: str) -> int:
            nonlocal k

            def check(cnt: dict, k: int) -> bool:
                for key in cnt:
                    val = cnt[key]
                    if not (val == 0 or val == k):
                        return False 
                return True 
            
            n = len(s)
            res = 0
            for m in range(1,27):
                #区间长度m*k
                if m*k > n:
                    break 
                #初始化
                cnt = collections.Counter()
                for i in range(m*k):
                    cnt[s[i]] += 1 
                if check(cnt, k):
                    res += 1
                #剩余的
                for i in range(1,n-m*k+1):
                    cnt[s[i-1]] -= 1
                    cnt[s[i+m*k-1]] += 1
                    if check(cnt, k):
                        res += 1 
            return res 
        
        i = 0
        n = len(word)
        res = 0
        while i < n:
            j = i + 1
            while j < n and abs(ord(word[j])-ord(word[j-1])) <= 2:
                j += 1
            res += f(word[i:j])
            i = j #更新i
        return res 

2.2 不定长度的滑动窗口(求最大值)

题目183. 无重复字符的最长子串

解题思路:模拟。注意更新滑动区间left时,用上一个重复字母的下标加1去更新。

C++代码如下,

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        int i = 0;
        int res = 0;
        while (i < n) {
            int j = i;
            unordered_map<char, int> map_char_idx;
            while (j < n && map_char_idx.count(s[j]) == 0) {
                map_char_idx[s[j]] = j;
                j += 1;
            }
            res = max(res, j - i);
            if (j < n) {
                i = map_char_idx[s[j]] + 1; //更新i
            } else {
                break;
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        res = 0
        i = 0 
        n = len(s)
        while i < n:
            j = i
            map_char_idx = collections.defaultdict(int)
            while j < n and s[j] not in map_char_idx:
                map_char_idx[s[j]] = j 
                j += 1
            res = max(res, j - i)
            if j < n:
                i = map_char_idx[s[j]] + 1 #更新i
            else:
                break
        return res 

题目191493. 删掉一个元素以后全为 1 的最长子数组

解题思路:把值为0的下标存储起来。遍历每一个下标,计数leftcntrightcnt

C++代码如下,

class Solution {
public:
    int longestSubarray(vector<int>& nums) {
        int n = nums.size();
        vector<int> zero_idxes;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 0) {
                zero_idxes.emplace_back(i);
            }
        }
        if (zero_idxes.size() == 0) { //特判
            return n-1;
        }

        int leftcnt = 0;
        int rightcnt = 0;
        int i = zero_idxes[0];
        i -= 1;
        while (i >= 0 && nums[i] == 1) {
            leftcnt += 1;
            i -= 1;
        }
        i = zero_idxes[0];
        i += 1;
        while (i < n && nums[i] == 1) {
            rightcnt += 1;
            i += 1;
        }
        int res = leftcnt + rightcnt;
        for (int k = 1; k < zero_idxes.size(); ++k) {
            leftcnt = rightcnt;
            rightcnt = 0;
            int i = zero_idxes[k] + 1;
            while (i < n && nums[i] == 1) {
                rightcnt += 1;
                i += 1;
            }
            res = max(res, leftcnt + rightcnt);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        n = len(nums)
        zero_idxes = []
        for i in range(n):
            if nums[i] == 0:
                zero_idxes.append(i)
        
        if len(zero_idxes) == 0: #特判
            return n-1

        res = 0
        leftcnt = 0
        rightcnt = 0
        #初始化
        i = zero_idxes[0]
        i -= 1
        while i >= 0 and nums[i] == 1:
            leftcnt += 1
            i -= 1
        i = zero_idxes[0]
        i += 1
        while i < n and nums[i] == 1:
            rightcnt += 1
            i += 1
        res = leftcnt + rightcnt 
        #剩余的
        for k in range(1,len(zero_idxes)):
            leftcnt = rightcnt 
            rightcnt = 0
            i = zero_idxes[k] + 1
            while i < n and nums[i] == 1:
                rightcnt += 1 
                i += 1
            res = max(res, leftcnt + rightcnt)
        return res 

题目202730. 找到最长的半重复子字符串

解题思路:先找到满足s[j] == s[j-1]的下标idxes,然后在开头加入0,在结尾加入len(s)。计算idxes[i]-idxes[i-1],存储在列表lengths中,然后记录lengths[i]+lengths[i+1]的最大值,注意特殊判断len(lengths)为1的情况。

C++代码如下,

class Solution {
public:
    int longestSemiRepetitiveSubstring(string s) {
        int n = s.size();
        vector<int> idxes = {0};
        for (int i = 1; i < n; ++i) {
            if (s[i] == s[i-1]) {
                idxes.emplace_back(i);
            }
        }
        idxes.emplace_back(n);
        vector<int> lengths;
        for (int i = 1; i < idxes.size(); ++i) {
            int length = idxes[i] - idxes[i-1];
            lengths.emplace_back(length);
        }
        if (lengths.size() == 1) {//特判
            return n;
        }
        int res = 0;
        for (int i = 1; i < lengths.size(); ++i) {
            int ans = lengths[i] + lengths[i-1];
            res = max(res, ans);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestSemiRepetitiveSubstring(self, s: str) -> int:
        n = len(s)
        idxes = [0]
        for j in range(1,n):
            if s[j] == s[j-1]:
                idxes.append(j)
        idxes.append(n)
        lengths = []
        for i in range(1,len(idxes)):
            length = idxes[i] - idxes[i-1]
            lengths.append(length)
        if len(lengths) == 1: #特判
            return n
        res = 0
        for i in range(1,len(lengths)):
            ans = lengths[i] + lengths[i-1]
            res = max(res, ans)
        return res 

题目21904. 水果成篮

解题思路:每次更新left时,更新为最后一棵树出现的下标,取两者中的较小值+1。

C++代码如下,

class Solution {
public:
    int totalFruit(vector<int>& a) {
        int n = a.size();
        int left = 0;
        int right = 0;
        unordered_map<int,int> map_val_idx;
        int res = 0;
        while (right < n) {
            if (map_val_idx.count(a[right]) > 0) {
                map_val_idx[a[right]] = right;
                res = max(res, right - left + 1);
            } else {
                if (map_val_idx.size() < 2) {
                    map_val_idx[a[right]] = right;
                    res = max(res, right - left + 1);
                } else {
                    vector<int> keys, vals;
                    for (auto [k, v] : map_val_idx) {
                        keys.emplace_back(k);
                        vals.emplace_back(v);
                    }
                    left = min(vals[0], vals[1]) + 1;
                    map_val_idx.clear();
                    map_val_idx[a[right-1]] = right-1;
                    map_val_idx[a[right]] = right;
                }
            }
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def totalFruit(self, a: List[int]) -> int:
        #更新left的时候,更新为最后一棵树出现的下标,取两者中的较小值+1
        n = len(a)
        left = 0
        right = 0
        map_val_idx = collections.defaultdict(int)
        map_val_idx[a[0]] = 0
        res = 0
        while right < n:
            if a[right] in map_val_idx:
                map_val_idx[a[right]] = right 
                res = max(res, right - left + 1)
            else:
                if len(map_val_idx) < 2:
                    map_val_idx[a[right]] = right 
                    res = max(res, right - left + 1)
                else:
                    keys = []
                    vals = []
                    for key in map_val_idx:
                        keys.append(key)
                        vals.append(map_val_idx[key])
                    left = min(vals) + 1
                    map_val_idx = collections.defaultdict(int)
                    map_val_idx[a[right-1]] = right-1
                    map_val_idx[a[right]] = right  

            right += 1 
        return res 

题目221695. 删除子数组的最大得分

解题思路:记滑动窗口的左右端点分别为ij,有visited = set(),如果a[j]出现过,则visited.discard(a[i]),直到a[j]没有出现过。

C++代码如下,

class Solution {
public:
    int maximumUniqueSubarray(vector<int>& a) {
        set<int> visited;
        int i = 0; //滑动窗口左端点
        int j = 0; //滑动窗口右端点
        int n = a.size();
        int currSum = 0;
        int res = 0;
        while (j < n) {
            while (visited.count(a[j]) > 0) {
                visited.erase(a[i]);
                currSum -= a[i];
                i += 1;
            }
            visited.insert(a[j]);
            currSum += a[j];
            res = max(res, currSum);
            j += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maximumUniqueSubarray(self, a: List[int]) -> int:
        visited = set()
        res = 0
        currSum = 0
        i = 0 #滑动窗口左端点
        j = 0 #滑动窗口右端点
        n = len(a)
        while j < n:
            while a[j] in visited:
                visited.discard(a[i])
                currSum -= a[i]
                i += 1
            visited.add(a[j]) 
            currSum += a[j]
            res = max(res, currSum)
            j += 1
        return res 

题目232958. 最多 K 个重复元素的最长子数组

不定长度的滑动区间算法经典题目!!!注意,一般该类题型的假设条件是区间长度不能过长,比如此题中的“最多K个重复元素”。

解题思路:滑动区间的右端点是逐渐递增的,利用这一点,更新left

C++代码如下,

class Solution {
public:
    int maxSubarrayLength(vector<int>& a, int k) {
        int n = a.size();
        int left = 0;
        int right = 0;
        int res = 0;
        unordered_map<int, int> cnt;
        while (right < n) {
            while (cnt[a[right]]+1 > k) {
                cnt[a[left]] -= 1;
                left += 1;
            }
            cnt[a[right]] += 1;
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxSubarrayLength(self, a: List[int], k: int) -> int:
        n = len(a)
        left = 0
        right = 0
        res = 0
        cnt = collections.defaultdict(int)
        while right < n:  
            while cnt[a[right]]+1 > k:
                cnt[a[left]] -= 1
                left += 1
            cnt[a[right]] += 1
            res = max(res, right - left + 1)
            right += 1
        return res 

题目242024. 考试的最大困扰度

解题思路:考虑left的更新。

C++代码如下,

class Solution {
public:
    int maxConsecutiveAnswers(string s, int k) {
        int n = s.size();
        int left = 0;
        int right = 0;
        int res = 0;
        unordered_map<int,int> cnt;

        function<bool(unordered_map<int,int>,int)> check =[&](unordered_map<int,int> cnt, int right) -> bool {
            int a = cnt['T'];
            int b = cnt['F'];
            if (s[right] == 'T') a += 1;
            else b += 1;
            return min(a,b) > k;
        };

        while (right < n) {
            while (check(cnt, right)) {
                cnt[s[left]] -= 1;
                left += 1;
            }
            cnt[s[right]] += 1;
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxConsecutiveAnswers(self, s: str, k: int) -> int:
        n = len(s)
        left = 0
        right = 0
        res = 0
        cnt = collections.defaultdict(int)

        def check(cnt: dict, right: int) -> bool:
            nonlocal s 
            a = cnt["T"]
            b = cnt["F"]
            if s[right] == "T":
                a += 1
            else:
                b += 1
            return min(a,b) > k

        while right < n:
            while check(cnt,right):
                cnt[s[left]] -= 1
                left += 1
            cnt[s[right]] += 1
            res = max(res, right-left+1)
            right += 1
        return res 

题目251004. 最大连续1的个数 III

解题思路:注意滑动区间左端点的更新。

C++代码如下,

class Solution {
public:
    int longestOnes(vector<int>& a, int k) {
        int n = a.size();
        int i = 0; //滑动区间左端点
        int j = 0; //滑动区间右端点
        int res = 0;
        int cnt = 0; //滑动区间内0的个数
        while (j < n) {
            while (cnt + (a[j] == 0) > k) {
                cnt -= a[i] == 0;
                i += 1;
            }
            cnt += a[j] == 0;
            //cout << "j = " << j << ", i = " << i << ", cnt = " << cnt << endl;
            res = max(res, j-i+1);
            j += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestOnes(self, a: List[int], k: int) -> int:
        n = len(a)
        i = 0 
        j = 0
        cnt = 0
        res = 0
        while j < n:
            while cnt + int(a[j] == 0) > k:
                cnt -= int(a[i] == 0)
                i += 1
            cnt += int(a[j] == 0)
            res = max(res, j - i + 1)
            j += 1 
        return res 

题目262401. 最长优雅子数组

解题思路:注意滑动区间左端点left的更新。

C++代码如下,

class Solution {
public:
    int longestNiceSubarray(vector<int>& a) {
        int n = a.size();
        int left = 0;
        int right = 0;
        int cnt[32] = {0};
        int res = 0;

        function<void(int,int[])> sub =[&] (int x, int cnt[]) -> void {
            for (int i = 0; i < 32; ++i) {
                cnt[i] -= (x >> i) & 1;
            }
            return;
        };

        function<void(int,int[])> add =[&] (int x, int cnt[]) -> void {
            for (int i = 0; i < 32; ++i) {
                cnt[i] += (x >> i) & 1;
            }
            return;
        };

        function<bool(int,int[])> check =[&] (int x, int cnt[]) -> bool {
            add(x, cnt);
            bool res = false;
            for (int i = 0; i < 32; ++i) {
                if (cnt[i] >= 2) {
                    res = true;
                    break;
                }
            }
            sub(x, cnt);
            return res;
        };

        while (right < n) {
            while (check(a[right], cnt)) {
                sub(a[left], cnt);
                left += 1;
            }
            add(a[right], cnt);
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestNiceSubarray(self, a: List[int]) -> int:
        n = len(a)
        left = 0
        right = 0
        cnt = [0] * 32
        res = 0

        def sub(x: int, cnt: list) -> None:
            for i in range(32):
                cnt[i] -= (x >> i) & 1
            return 
        
        def add(x: int, cnt: list) -> None:
            for i in range(32):
                cnt[i] += (x >> i) & 1 
            return 
        
        def check(x: int, cnt: list) -> bool:
            add(x, cnt)
            res = False 
            for i in range(32):
                if cnt[i] >= 2:
                    res = True
                    break
            sub(x, cnt)
            return res 

        while right < n:
            while check(a[right], cnt):
                sub(a[left], cnt)
                left += 1
            add(a[right], cnt)
            res = max(res, right-left+1)
            right += 1
        return res 

题目271658. 将 x 减到 0 的最小操作数

解题思路:注意滑动区间左端点left的更新,以及滑动区间要包含原数组i=0i=n-1下标处的元素。

C++代码如下,

class Solution {
public:
    int minOperations(vector<int>& a, int x) {
        int n = a.size();
        vector<int> b = a;
        b.insert(b.end(), a.begin(), a.end());
        int left = 0;
        int right = 0;
        int cur = 0;
        int res = n + 1;
        while (right < n * 2) {
            while (cur + b[right] > x) {
                cur -= b[left];
                left += 1;
            }
            cur += b[right];
            if (cur == x && right < n + left) {
                if ((left == 0) || (left <= n-1 && right >= n-1)) {
                    res = min(res, right-left+1);
                }
            }
            right += 1;
        }
        if (res == n + 1) {
            res = -1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def minOperations(self, a: List[int], x: int) -> int:
        a += a 
        n = len(a)
        left = 0
        right = 0 
        res = n // 2 + 1
        cur = 0
        while right < n:
            while cur + a[right] > x:
                cur -= a[left]
                left += 1
            cur += a[right]
            if cur == x and right < left + n: 
                if left == 0 or (left <= n//2-1 and right >= n//2-1):
                    res = min(res, right-left+1)
            #print(f"left={left},right={right},cur={cur}.")
            right += 1
        if res == n // 2 + 1: #特判无结果
            res = -1
        return res 

题目281838. 最高频元素的频数

解题思路:注意滑动区间左端点left的更新。

C++代码如下,

class Solution {
public:
    int maxFrequency(vector<int>& a, int k) {
        sort(a.begin(), a.end()); //排序
        
        int n = a.size();
        int left = 0;
        int right = 0;
        int res = 0;
        long long cur = 0;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            return ((long long)a[right] * (right-left) - cur) > k;
        };

        while (right < n) {
            while (check(left,right)) {
                cur -= a[left];
                left += 1;
            }
            cur += a[right];
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxFrequency(self, a: List[int], k: int) -> int:
        a.sort()
        cur = 0
        res = 1
        left = 0
        right = 0
        n = len(a)

        def check(right: int) -> bool:
            nonlocal cur, a, k, left 
            res = a[right] * (right-left) - cur > k
            return res  

        while right < n:
            while check(right):
                cur -= a[left]
                left += 1
            cur += a[right]
            res = max(res, right-left+1)
            right += 1
        return res 

题目292516. 每种字符至少取 K 个

解题思路1:本题是每种字符至少k个,求最短滑动窗口,因此可以这样做。

C++代码如下,

class Solution {
public:
    int takeCharacters(string s, int k) {
        if (k == 0) { //特判k=0的corner case
            return 0;
        }
        int n = s.size();
        s += s;
        int left = 0;
        int right = 0;
        int cnt[3] = {0};
        int res = n + 1;

        function<bool(int[])> check =[&] (int cnt[]) -> bool {
            return cnt[0] >= k && cnt[1] >= k && cnt[2] >= k;
        };

        while (right < n * 2) {
            cnt[s[right]-'a'] += 1;
            if (check(cnt)) {
                while (left <= right && check(cnt)) {
                    if (left == 0 || (left <= n-1 && right >= n-1)) {
                        res = min(res, right-left+1);
                    }
                    cnt[s[left]-'a'] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt[s[left]-'a'] += 1;
            }
            right += 1;
        }

        if (res == n+1) {
            res = -1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        if k == 0: #特判
            return 0

        n = len(s)
        s += s 
        left = 0
        right = 0
        cnt = {'a':0,'b':0,'c':0}
        res = n + 1

        def check(cnt: dict) -> bool:
            return cnt["a"] >= k and cnt["b"] >= k and cnt["c"] >= k

        while right < 2 * n:
            cnt[s[right]] += 1
            while left <= right and check(cnt):
                if left == 0 or (left <= n-1 and right >= n-1):
                    res = min(res, right-left+1)
                cnt[s[left]] -= 1
                left += 1
            left -= 1
            cnt[s[left]] += 1
            right += 1

        if res == n+1:
            res = -1
        return res 

解题思路2:本题是每种字符>=k个,求最短边缘滑动窗口,逆向思维,每个字符>=k个,求最长中间滑动窗口,答案返回n-它的值

C++代码如下,

class Solution {
public:
    int takeCharacters(string s, int k) {
        if (k == 0) { //特判k=0的情况
            return 0;
        }
        
        int n = s.size();
        int left = 0;
        int right = 0;
        int ans = 0;
        int cnt[3] = {0};
        for (auto c : s) {
            cnt[c-'a'] += 1;
        }

        if (cnt[0] < k || cnt[1] < k || cnt[2] < k) { //特判无法取到的情况
            return -1;
        }

        while (right < n) {
            while (left <= right && cnt[s[right]-'a']-1 < k) {
                cnt[s[left]-'a'] += 1;
                left += 1;
            }
            cnt[s[right]-'a'] -= 1;
            ans = max(ans, right-left+1);
            right += 1;
        }

        return n-ans;
    }
};

python3代码如下,

class Solution:
    def takeCharacters(self, s: str, k: int) -> int:
        if k == 0: #特判k=0的情况
            return 0 
        
        n = len(s)
        left = 0
        right = 0
        ans = 0
        cnt = collections.Counter(s)

        if cnt['a'] < k or cnt['b'] < k or cnt['c'] < k: #特判无法取到的情况
            return -1

        while right < n:
            while cnt[s[right]]-1 < k:
                cnt[s[left]] += 1
                left += 1
            cnt[s[right]] -= 1
            ans = max(ans, right-left+1)
            right += 1
        
        return n-ans 

题目302831. 找出最长等值子数组

解题思路:先处理出map_val_idxs,然后对每个idxs进行滑动窗口算法。

C++代码如下,

class Solution {
public:
    int longestEqualSubarray(vector<int>& a, int k) {
        unordered_map<int, vector<int>> idxs;
        for (int i = 0; i < a.size(); ++i) {
            idxs[a[i]].emplace_back(i);
        }

        int res = 1;
        for (auto [_,b] : idxs) {
            if (b.size() <= res) continue;
            int left = 0;
            int right = 0;
            while (right < b.size()) {
                while (left <= right && (b[right]-b[left]+1)-(right-left+1)>k) {
                    left += 1;
                }
                res = max(res, right-left+1);
                right += 1;
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestEqualSubarray(self, a: List[int], k: int) -> int:
        idxs = collections.defaultdict(list)
        for i,x in enumerate(a):
            idxs[x].append(i)
        
        res = 1
        for b in idxs.values():
            if len(b) <= res:
                continue 
            
            left = 0
            right = 0
            while right < len(b):
                while left <= right and (b[right]-b[left]+1) - (right-left+1) > k:
                    left += 1
                res = max(res, right-left+1)
                right += 1

        return res 

题目312106. 摘水果

解题思路:如何想到更新滑动窗口左端点left的判断条件。“先往左走,再往右走”行不通,并且“先往右走,再往左走”也行不通。

C++代码如下,

class Solution {
public:
    int maxTotalFruits(vector<vector<int>>& a, int startPos, int k) {
        vector<int> t = {startPos-k};
        auto iter = lower_bound(a.begin(), a.end(), t);
        int left = distance(a.begin(), iter);
        t[0] = startPos + 1;
        iter = lower_bound(a.begin(), a.end(), t);
        int right = distance(a.begin(), iter);
        long long res = 0;
        long long s = 0;
        for (int i = left; i < right; ++i) {
            res += a[i][1];
            s += a[i][1];
        }
        //cout << "res = " << res << endl; 
        //cout << "left = " << left << ", right = " << right << endl;

        while (right < a.size() && a[right][0] <= startPos+k) {
            while ((startPos-a[left][0] + a[right][0]-a[left][0]) > k && (a[right][0]-startPos + a[right][0]-a[left][0]) > k) {
                s -= a[left][1];
                left += 1;
            }
            s += a[right][1];
            res = max(res, s);
            //cout << "left = " << left << ", right = " << right << endl;
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxTotalFruits(self, a: List[List[int]], startPos: int, k: int) -> int:
        left = bisect.bisect_left(a, [startPos-k])
        right = bisect.bisect_left(a, [startPos+1])
        res = 0
        s = 0
        for i in range(left,right):
            res += a[i][1]
            s += a[i][1]
        
        while right < len(a) and a[right][0] <= startPos + k:
            while startPos-a[left][0] + a[right][0]-a[left][0] > k and a[right][0]-startPos + a[right][0]-a[left][0] > k:
                s -= a[left][1]
                left += 1
            s += a[right][1]
            res = max(res, s)
            right += 1
        
        return res 

题目321610. 可见点的最大数目

解题思路:应用题。模拟+滑动窗口。

C++代码如下,

class Solution {
public:
    int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) {
        int res = 0;
        vector<double> a;
        for (auto point : points) {
            if (point[0] == location[0] && point[1] == location[1]) {
                res += 1;
            } else {
                double x = point[0]-location[0], y = point[1]-location[1];
                double theta = atan2(y, x);
                theta *= 180.0 / M_PI;
                if (theta < 0.0) {
                    theta += 360.0;
                }
                a.emplace_back(theta);
            }
        }

        double angle_f = angle;
        sort(a.begin(), a.end());
        int n = a.size();
        for (int i = 0; i < n; ++i) {
            a.emplace_back(a[i]+360.0);
        }

        int ans = 0;
        int left = 0;
        int right = 0;
        while (right < 2 * n) {
            while (left <= right && a[right]-a[left] > angle_f) {
                left += 1;
            }
            if (a[right]-a[left] <= 360.0) {
                ans = max(ans, right-left+1);
            }
            right += 1;
        }
        res = res + ans;
        return res;
    }
};

python3代码如下,

import math 

class Solution:
    def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
        res = 0
        a = []
        for point in points:
            if point == location:
                res += 1
            else:
                vec = [point[0]-location[0], point[1]-location[1]]
                theta = math.atan2(vec[1], vec[0])
                theta = math.degrees(theta) 
                a.append(theta)
        angle = float(angle) #将angle转为浮点类型

        #print(f"a = {a}.")

        n = len(a)
        for i in range(n):
            if a[i] < 0:
                a[i] = 360.0 + a[i]
        a.sort()
        for i in range(n):
            a.append(a[i]+360.0)

        #求观测到的最大点数

        left = 0
        right = 0
        ans = 0
        #print(f"a = {a}.")
        while right < 2 * n:
            while left <= right and a[right]-a[left] > angle:
                left += 1
            if a[right]-a[left] <= 360:
                ans = max(ans, right-left+1)
            right += 1
        #print(f"res = {res}, ans = {ans}.")
        res = res + ans 
        return res 

题目332781. 最长合法子字符串的长度

解题思路:注意forbidden中元素的最大长度为10。

C++代码如下,

class Solution {
public:
    int longestValidSubstring(string word, vector<string>& forbidden) {
        set<string> b(forbidden.begin(), forbidden.end());
        int n = word.size();
        int left = 0;
        int right = 0;
        int res = 0;

        function<bool(int,int)> check =[&](int left, int right) -> bool {
            int kmin = max(left, right-9);
            for (int k = kmin; k <= right; ++k) {
                string t = word.substr(k,right-k+1);
                if (b.count(t) != 0) {
                    return true; //明令禁止
                }
            }
            return false; //
        };

        while (right < n) {
            while (left <= right && check(left,right)) {
                left += 1;
            }
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestValidSubstring(self, word: str, forbidden: List[str]) -> int:
        n = len(word)
        left = 0
        right = 0
        res = 0
        forbidden = set(forbidden)

        def check(left:int, right:int) -> bool:
            nonlocal word, forbidden
            kmin = max(left, right-9)
            for k in range(kmin,right+1):
                t = word[k:right+1]
                if t in forbidden:
                    return True #禁止
            return False #

        while right < n:
            while left <= right and check(left,right):
                left += 1
            res = max(res, right-left+1)
            right += 1
        return res 

题目342968. 执行操作使频率分数最大

解题思路:中位数贪心,即使得滑动区间[left,right]内分数最大的xa[(left+right)//2]。起源是求最小距离和。

C++代码如下,

class Solution {
public:
    int maxFrequencyScore(vector<int>& a, long long k) {
        sort(a.begin(), a.end());
        a.insert(a.begin(), 0);
        int n = a.size();
        vector<long long> s(n, 0);
        for (int i = 1; i < n; ++i) {
            s[i] = s[i-1] + a[i];
        }

        int left = 1;
        int right = 1;
        int res = 1;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            long long res = 0;
            int i = (left + right) / 2;
            res += (long long)a[i] * (i-left+1) - (s[i]-s[left-1]); //[left,i]
            res += (s[right]-s[i]) - (long long)a[i] * (right-i); //[i+1,right]
            return res > k; //违法
        };

        while (right < n) {
            while (left <= right && check(left, right)) {
                left += 1;
            }
            res = max(res, right-left+1);
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxFrequencyScore(self, a: List[int], k: int) -> int:
        #中位数贪心
        a.sort() #排序
        a.insert(0, 0)
        n = len(a)
        s = [0] * n
        for i in range(1,n):
            s[i] += s[i-1] + a[i]
        
        left = 1
        right = 1
        res = 1
    
        #print(f"a = {a}.")
        #print(f"n//2 = {n//2}.")

        def check(left: int, right: int) -> bool:
            nonlocal n, s, a
            i = (left + right) // 2
            x = a[i] 
            res = 0
            res = a[i] * (i-left+1) - (s[i]-s[left-1]) #[left,i] 
            res += (s[right]-s[i]) - x * (right-i) #[i+1,right]
            return res > k #非法

        while right < n:
            while left <= right and check(left, right):
                left += 1
            res = max(res, right-left+1)
            #print(f"left={left},right={right}.")
            right += 1
        return res 

题目35395. 至少有 K 个重复字符的最长子串

解题思路:先枚举滑动窗口内出现的字符种类数目,然后再枚举滑动窗口的左右端点leftright

C++代码如下,

class Solution {
public:
    int longestSubstring(string s, int k) {
        int n = s.size();
        int res = 0;
        for (int t = 1; t <= 26; ++t) {
            //t表示滑动窗口内的字符种类数目
            int left = 0;
            int right = 0;
            int less = 0; //<k的字符种类数目
            int tot = 0; //当前滑动窗口内的字符种类数目
            int cnt[26] = {0};
            while (right < n) {
                int i = s[right]-'a';
                cnt[i] += 1;
                if (cnt[i] == 1) {
                    less += 1;
                    tot += 1;
                }
                if (cnt[i] == k) {
                    less -= 1;
                }

                while (tot > t) {
                    int j = s[left] - 'a';
                    cnt[j] -= 1;
                    if (cnt[j] == 0) {
                        less -= 1;
                        tot -= 1;
                    }
                    if (cnt[j] == k-1) {
                        less += 1;
                    }

                    left += 1;
                }

                if (less == 0) {
                    res = max(res, right-left+1);
                }

                right += 1;
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestSubstring(self, s: str, k: int) -> int:
        n = len(s)
        res = 0
        for t in range(1,27):
            #滑动窗口内字符种类数目为t
            left = 0
            right = 0 
            tot = 0 #当前滑动滑动窗口内的字符种类数目
            less = 0 #当前滑动窗口内字符<k的种类数目
            cnt = [0] * 26
            while right < n:
                i = ord(s[right])-ord('a')
                cnt[i] += 1
                if cnt[i] == 1:
                    tot += 1
                    less += 1
                
                if cnt[i] == k:
                    less -= 1
                
                while tot > t:
                    j = ord(s[left])-ord('a')
                    cnt[j] -= 1

                    if cnt[j] == 0:
                        tot -= 1
                        less -= 1
                    
                    if cnt[j] == k-1:
                        less += 1

                    left += 1

                if less == 0:
                    res = max(res, right-left+1)

                right += 1
        return res 

题目361763. 最长的美好子字符串

解题思路:数据量小,模拟即可。

C++代码如下,

class Solution {
public:
    string longestNiceSubstring(string s) {
        int n = s.size();
        int res1 = 0;

        function<bool(string)> check =[&] (string t) -> bool {
            int cnt1[26] = {0};
            int cnt2[26] = {0};
            for (char c : t) {
                if (c >= 'a' && c <= 'z') {
                    cnt1[c-'a'] += 1;
                } else {
                    cnt2[c-'A'] += 1;
                }
            }

            for (int i = 0; i < 26; ++i) {
                if (cnt1[i] * cnt2[i] == 0) {
                    if (cnt1[i] > 0 || cnt2[i] > 0) {
                        return false;
                    }
                }
            }

            return true;
        };

        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                string t = s.substr(i, j-i+1);
                if (check(t)) {
                    res1 = max(res1, j-i+1);
                }
            }
        }
        //cout << "res1 = " << res1 << endl;

        string res2 = "";
        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                string t = s.substr(i,j-i+1);
                if (check(t)) {
                    if (j-i+1 == res1) {
                        return t;
                    }
                }
            }
        }
        return "";
    }
};

python3代码如下,

class Solution:
    def longestNiceSubstring(self, s: str) -> str:
        n = len(s)
        res1 = 0

        def check(t: str) -> bool:
            t1 = "" #小写部分
            t2 = "" #大写部分
            for c in t:
                if c.islower():
                    t1 += c 
                else:
                    t2 += c 
            t1 = sorted(set(t1))
            t2 = sorted(set(t2))

            #print(f"t1={t1},t2={t2}.")

            if len(t1) != len(t2):
                return False 
            for i in range(len(t1)):
                if t1[i].lower() != t2[i].lower():
                    return False 
            return True 

        for i in range(n):
            for j in range(i+1,n+1):
                t = s[i:j]
                if check(t):
                    res1 = max(res1, len(t))
        #print(f"res1={res1}.")
        res2 = ""
        for i in range(n):
            for j in range(i+1,n+1):
                t = s[i:j]
                if check(t):
                    if len(t) == res1:
                        return t 
        return ""

2.3 不定长度的滑动窗口(求最小值)

题目37209. 长度最小的子数组

解题思路:滑动窗口套路题,注意更新滑动窗口的左端点left

C++代码如下,

class Solution {
public:
    int minSubArrayLen(int k, vector<int>& a) {
        a.insert(a.begin(), 0);
        int n = a.size();
        vector<long long> s(n, 0);
        for (int i = 1; i < n; ++i) {
            s[i] = s[i-1] + a[i];
        }

        int left = 1;
        int right = 1;
        int res = n+1;
        while (right < n) {
            while (left <= right && s[right]-s[left-1] >= k) {
                res = min(res, right-left+1);
                left += 1;
            }
            right += 1;
        }
        if (res == n+1) {
            res = 0;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def minSubArrayLen(self, k: int, a: List[int]) -> int:
        a.insert(0, 0)
        n = len(a)
        s = [0] * n 
        for i in range(1,n):
            s[i] = s[i-1] + a[i]
        
        left = 1
        right = 1
        res = n+1
        while right < n:
            while left <= right and s[right]-s[left-1] >= k:
                res = min(res, right-left+1)
                left += 1
            right += 1
        if res == n+1:
            res = 0
        return res 

题目381234. 替换子串得到平衡字符串

解题思路:满足cnt1[key]>=cnt[key]的最小滑动区间。

C++代码如下,

class Solution {
public:
    int balancedString(string s) {
        unordered_map<char, int> cnt;
        for (auto c : s) {
            cnt[c] += 1;
        }
        int n = s.size();
        for (auto [k,v] : cnt) {
            cnt[k] -= n / 4;
        }

        function<bool(unordered_map<char,int>)> special_check =[&] (unordered_map<char,int> cnt) -> bool {
            for (auto [key, value] : cnt) {
                if (value > 0) {
                    return false;
                }
            }
            return true;
        };

        if (special_check(cnt)) { //特判cnt中全为0
            return 0;
        }

        //cnt1[key] >= cnt[key]的最小滑动区间
        unordered_map<char, int> cnt1;
        int left = 0;
        int right = 0;
        int res = n + 1;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            cnt1[s[right]] += 1;
            bool res = true;
            for (auto [key, val] : cnt) {
                if (cnt1[key] < cnt[key]) {
                    res = false;
                    break;
                }
            }
            cnt1[s[right]] -= 1;
            return res;
        };

        while (right < n) {
            if (check(left, right)) {
                while (left <= right && check(left, right)) {
                    res = min(res, right-left+1);
                    cnt1[s[left]] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt1[s[left]] += 1;
                cnt1[s[right]] += 1;
            } else {
                cnt1[s[right]] += 1;
            }

            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def balancedString(self, s: str) -> int:
        n = len(s)
        cnt = collections.Counter(s)
        for key in cnt:
            cnt[key] -= n // 4
            cnt[key] = max(0, cnt[key])
        #求s中的最小长度,使得cnt1[key] >= cnt[key]

        def special_check(cnt: dict) -> bool:
            for key in cnt:
                if cnt[key] > 0:
                    return False 
            return True 

        #特判cnt内的所有元素都为0
        if special_check(cnt):
            return 0

        cnt1 = collections.defaultdict(int)
        right = 0
        left = 0
        res = n+1

        def check(left: int, right: int) -> bool:
            nonlocal cnt1, cnt 
            cnt1[s[right]] += 1
            res = True
            for key in cnt:
                if cnt1[key] < cnt[key]:
                    res = False 
                    break 
            cnt1[s[right]] -= 1
            return res 

        while right < n:
            if check(left, right):
                while left <= right and check(left, right):
                    res = min(res, right-left+1)
                    cnt1[s[left]] -= 1
                    left += 1 
                left -= 1
                cnt1[s[left]] += 1
                cnt1[s[right]] += 1
            else:
                cnt1[s[right]] += 1

            right += 1
        return res 

题目391574. 删除最短的子数组使剩余数组有序

解题思路:先枚举right,再枚举left。

C++代码如下,

class Solution {
public:
    int findLengthOfShortestSubarray(vector<int>& arr) {
        int n = arr.size();
        int right = n-1;
        while (right-1 >= 0 && arr[right-1] <= arr[right]) {
            right -= 1;
        }
        if (right == 0) { //特判
            return 0;
        }
        int res = right;
        int left = 0;
        while (left == 0 || arr[left-1] <= arr[left]) {
            while (right < n && arr[left] > arr[right]) {
                right += 1;
            }
            //此时,arr[left]<=arr[right]
            res = min(res, right-left-1);
            left += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
        #先枚举right,再枚举left 
        n = len(arr) 
        right = n-1
        while right-1 >= 0 and arr[right-1] <= arr[right]:
            right -= 1
        if right == 0: #特判特殊情况 
            return 0
        res = right #初始答案
        left = 0
        while left == 0 or arr[left-1] <= arr[left]:
            while right < n and arr[right] < arr[left]:
                right += 1
            #此时arr[left] <= arr[right]
            res = min(res, right-left-1)
            left += 1
        return res 

题目4076. 最小覆盖子串

解题思路:滑动窗口算法。

C++代码如下,

//尽量少使用s.substr()
class Solution {
public:
    string minWindow(string s, string t) {
        int cnt1[128] = {0};
        int cnt2[128] = {0}; 
        for (auto c : t) cnt2[c] += 1;
        int right = 0;
        int left = 0;
        int n = s.size();
        int res1 = 0;
        int res2 = n;
        

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            cnt1[s[right]] += 1;
            bool res = true;
            for (int key = 0; key < 128; ++key) {
                if (cnt1[key] < cnt2[key]) {
                    res = false;
                    break;
                }
            }
            cnt1[s[right]] -= 1;
            return res;
        };

        while (right < n) {
            if (check(left, right)) {
                while (left <= right && check(left, right)) {
                    if (right-left+1 < res2-res1+1) {
                        res1 = left;
                        res2 = right;
                    }
                    cnt1[s[left]] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt1[s[left]] += 1;
                cnt1[s[right]] += 1;
            } else {
                cnt1[s[right]] += 1;
            }
            right += 1;
        }

        if (res1 == 0 && res2 == n) { //特判
            return "";
        }
        return s.substr(res1,res2-res1+1);
    }
};

python3代码如下,

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        cnt1 = collections.defaultdict(int)
        cnt2 = collections.Counter(t)
        left = 0
        right = 0
        n = len(s)
        res1 = n + 1
        res2 = ""

        def check(left: int, right: int) -> bool:
            nonlocal cnt1, cnt2 
            cnt1[s[right]] += 1
            res = True 
            for key in cnt2:
                if cnt1[key] < cnt2[key]:
                    res = False 
                    break 
            cnt1[s[right]] -= 1 
            return res 

        while right < n:
            if check(left,right):
                while check(left,right):
                    if right-left+1 < res1:
                        res1 = right-left+1 
                        res2 = s[left:right+1]
                    cnt1[s[left]] -= 1 
                    left += 1
                left -= 1
                cnt1[s[left]] += 1
                cnt1[s[right]] += 1
            else:
                cnt1[s[right]] += 1 
            right += 1
        return res2 

题目41面试题 17.18. 最短超串

解题思路:常规解法。

C++代码如下,

class Solution {
public:
    vector<int> shortestSeq(vector<int>& big, vector<int>& small) {
        set<int> a(small.begin(), small.end());
        map<int, int> cnt;
        int n = big.size();
        int left = 0;
        int right = 0;
        int res1 = 0, res2 = n;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            for (int key : a) {
                if (cnt[key] == 0) return false;
            }
            return true;
        };

        while (right < n) {
            cnt[big[right]] += 1;
            if (check(left,right)) {
                while (check(left,right)) {
                    if (right-left+1 < res2-res1+1) {
                        res1 = left;
                        res2 = right;
                    }
                    cnt[big[left]] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt[big[left]] += 1;
            }
            right += 1;
        }
        if (res1 == 0 && res2 == n) { //特判
            return {};
        } else {
            return {res1, res2};
        }
    }
};

python3代码如下,

class Solution:
    def shortestSeq(self, big: List[int], small: List[int]) -> List[int]:
        a = set(small)
        b = set(big)
        if a & b != a: #特判
            return []
        cnt = collections.defaultdict(int)
        n = len(big)
        left = 0
        right = 0
        res1 = 0
        res2 = n

        def check(left: int, right: int) -> bool:
            nonlocal cnt, a 
            for key in a:
                if cnt[key] == 0:
                    return False 
            return True 

        while right < n:
            cnt[big[right]] += 1
            if check(left,right):
                while check(left,right):
                    if right-left+1 < res2-res1+1:
                        res1 = left 
                        res2 = right 
                    cnt[big[left]] -= 1
                    left += 1
                left -= 1
                cnt[big[left]] += 1
            right += 1
        return [res1, res2]

2.4 不定长滑动窗口(求子数组个数)

题目422799. 统计完全子数组的数目

解题思路:套路题。

C++代码如下,

class Solution {
public:
    int countCompleteSubarrays(vector<int>& nums) {
        set<int> a(nums.begin(), nums.end());
        int left = 0, right = 0, n = nums.size();
        int res = 0;
        map<int, int> cnt;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            for (int key : a) {
                if (cnt[key] == 0) return false;
            }
            return true;
        };

        while (right < n) {
            cnt[nums[right]] += 1;
            if (check(left, right)) {
                while (check(left, right)) {
                    cnt[nums[left]] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt[nums[left]] += 1;
                res += left + 1;
            }
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countCompleteSubarrays(self, nums: List[int]) -> int:
        a = set(nums)
        n = len(nums)
        res = 0
        left = 0
        right = 0
        cnt = collections.defaultdict(int)

        def check(left: int, right: int) -> bool:
            nonlocal cnt, a 
            for key in a:
                if cnt[key] == 0:
                    return False 
            return True 

        while right < n:
            cnt[nums[right]] += 1 
            if check(left, right):
                while check(left, right):
                    cnt[nums[left]] -= 1 
                    left += 1
                left -= 1
                cnt[nums[left]] += 1 
                res += left + 1
            right += 1
        return res 

题目43713. 乘积小于 K 的子数组

解题思路:套路题。

C++代码如下,

class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        long long multis = 1;
        int n = nums.size();
        int left = 0, right = 0;
        int res = 0;
        while (right < n) {
            multis *= nums[right];
            if (multis < k) {
                res += right - left + 1;
            } else {
                while (left <= right && multis >= k) {
                    multis /= nums[left];
                    left += 1;
                }
                if (left <= right) {
                    res += right - left + 1;
                }
            } 
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        #以right为区间右端点(同时为子数组右端点),乘积<k的的子数组数目
        n = len(nums)
        left = 0
        right = 0
        multis = 1.0
        res = 0
        while right < n:
            multis *= nums[right]
            if multis < float(k):
                res += right - left + 1
            else:
                while left <= right and multis >= float(k):
                    multis /= nums[left]
                    left += 1
                if left <= right:
                    res += right - left + 1
            right += 1
        return res 

题目441358. 包含所有三种字符的子字符串数目

解题思路:滑动窗口套路题。

C++代码如下,

class Solution {
public:
    int numberOfSubstrings(string s) {
        int n = s.size();
        int left = 0, right = 0;
        map<char, int> cnt = {{'a',0},{'b',0},{'c',0}};
        int res = 0;

        function<bool(int,int)> check =[&] (int left, int right) -> bool {
            for (auto [key, value] : cnt) {
                if (value == 0) return false;
            }
            return true;
        };

        while (right < n) {
            cnt[s[right]] += 1;
            if (check(left,right)) {
                while (check(left,right)) {
                    cnt[s[left]] -= 1;
                    left += 1;
                }
                left -= 1;
                cnt[s[left]] += 1;
                res += left + 1;
            }
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numberOfSubstrings(self, s: str) -> int:
        cnt = {'a':0, 'b':0, 'c':0}
        n = len(s)
        left = 0
        right = 0
        res = 0

        def check(left: int, right: int) -> bool:
            nonlocal cnt 
            for key in cnt:
                if cnt[key] == 0:
                    return False 
            return True 

        while right < n:
            cnt[s[right]] += 1
            if check(left, right):
                while check(left, right):
                    cnt[s[left]] -= 1
                    left += 1
                left -= 1
                cnt[s[left]] += 1 
                res += left + 1                
            right += 1
        return res 

题目452962. 统计最大元素出现至少 K 次的子数组

解题思路:滑动窗口套路题。

C++代码如下,

class Solution {
public:
    long long countSubarrays(vector<int>& nums, int k) {
        long long res = 0;
        int n = nums.size();
        int cnt = 0;
        int target = -1;
        for (auto x : nums) target = max(target, x);
        int left = 0, right = 0;
        while (right < n) {
            if (nums[right] == target) cnt += 1;

            if (cnt >= k) {
                while (left <= right && cnt >= k) {
                    if (nums[left] == target) cnt -= 1;
                    left += 1;
                }
                left -= 1;
                if (nums[left] == target) cnt += 1;
                res += left + 1;
            }

            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        #注意读题,是某个数的出现次数>=k
        target = max(nums)
        cnt = 0
        n = len(nums)
        left = 0
        right = 0
        res = 0
        while right < n:
            if nums[right] == target:
                cnt += 1
            if cnt >= k:
                while left <= right and cnt >= k:
                    if nums[left] == target:
                        cnt -= 1
                    left += 1
                left -= 1
                if nums[left] == target:
                    cnt += 1
                res += left + 1            
            right += 1
        return res 

题目462302. 统计得分小于 K 的子数组数目

解题思路:滑动窗口套路题。

C++代码如下,

class Solution {
public:
    long long countSubarrays(vector<int>& nums, long long k) {
        int n = nums.size();
        int left = 0;
        int right = 0;
        long long cur = 0;
        long long res = 0;
        while (right < n) {
            cur += nums[right];
            while (left <= right && cur * (right-left+1) >= k) {
                cur -= nums[left];
                left += 1;
            }
            //cout << "left = " << left << ", right = " << right << endl;
            if (left <= right) res += right - left + 1;
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        n = len(nums)
        left = 0
        right = 0
        cur = 0
        res = 0
        while right < n:
            cur += nums[right]
            while left <= right and cur * (right-left+1) >= k:
                cur -= nums[left]
                left += 1
            if left <= right:
                res += right - left + 1 
            right += 1
        return res 

题目472537. 统计好子数组的数目

解题思路:滑动窗口+组合数公式。

C++代码如下,

class Solution {
public:
    long long countGood(vector<int>& nums, int k) {
        int n = nums.size();
        int left = 0, right = 0;
        long long res = 0;
        long long cur = 0;
        map<int,int> cnt;
        while (right < n) {
            cnt[nums[right]] += 1;
            cur += cnt[nums[right]]-1;
            if (cur >= k) {
                while (left <= right && cur >= k) {
                    cnt[nums[left]] -= 1;
                    cur -= cnt[nums[left]];
                    left += 1;
                }
                if (left <= right) {
                    left -= 1;
                    cnt[nums[left]] += 1;
                    cur += cnt[nums[left]]-1;
                    res += left + 1;
                }
            }
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countGood(self, nums: List[int], k: int) -> int:
        #c[n][2]-c[n-1][2]=n-1
        n = len(nums)
        left = 0
        right = 0
        cnt = collections.defaultdict(int)
        cur = 0
        res = 0
        while right < n:
            cnt[nums[right]] += 1
            cur += cnt[nums[right]]-1
            if cur >= k:
                while left <= right and cur >= k:
                    cnt[nums[left]] -= 1
                    cur -= cnt[nums[left]]
                    left += 1
                if left <= right:
                    left -= 1
                    cnt[nums[left]] += 1
                    cur += cnt[nums[left]]-1
                    res += left + 1
                    #print(f"left = {left}, right = {right}.")
            right += 1
        return res 

题目482762. 不间断子数组

解题思路:滑动窗口套路题。

C++代码如下,

class Solution {
public:
    long long continuousSubarrays(vector<int>& nums) {
        int n = nums.size();
        int left = 0, right = 0;
        long long res = 0;
        map<int,int> cnt;
        while (right < n) {
            cnt[nums[right]] += 1;
            while (left <= right && cnt.rbegin()->first-cnt.begin()->first > 2) {
                cnt[nums[left]] -= 1;
                if (cnt[nums[left]] == 0) {
                    cnt.erase(nums[left]);
                }
                left += 1;
            }
            res += right - left + 1;
            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def continuousSubarrays(self, nums: List[int]) -> int:
        n = len(nums)
        res = 0
        left = 0
        right = 0
        cnt = collections.Counter()
        while right < n:
            cnt[nums[right]] += 1
            while left <= right and max(cnt)-min(cnt) > 2:
                cnt[nums[left]] -= 1
                if cnt[nums[left]] == 0:
                    del cnt[nums[left]]
                left += 1
            if left <= right:
                res += right-left+1
            right += 1
        return res 

题目492972. 统计移除递增子数组的数目 II

解题思路:先考虑a[left] < a[left+1],删除后缀, (1)[left+1,n-1] (2)[left,n-1] …… (left+2)[0,n-1]left+2个移除递增子数组。考虑满足a[right] < a[right+1]条件的每个right,找到a[left] < a[right],删除, (1)[left+1,right-1] (2)[left, right-1] …… (left+2)[0,right-1]left+2个移除递增子数组。

C++代码如下,

class Solution {
public:
    long long incremovableSubarrayCount(vector<int>& a) {
        int n = a.size();
        int left = 0;
        while (left+1 < n && a[left] < a[left+1]) {
            left += 1;
        }
        if (left == n-1) { //特判数组a原本就是严格单调递增数组
            long long res = (n+1)*n/2;
            return res;
        }
        long long res = left + 2;
        int right = n-1;
        while (right == n -1 || a[right] < a[right+1]) {
            while (left >= 0 && a[left] >= a[right]) {
                left -= 1;
            }
            res += left + 2;
            right -= 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def incremovableSubarrayCount(self, a: List[int]) -> int:
        n = len(a)
        left = 0
        right = n-1
        while left+1 < n and a[left] < a[left+1]:
            left += 1
        if left == n-1: #特判是严格单调递增数组
            res = n * (n+1) // 2
            return res 
        
        res = left + 2
        while right == n-1 or a[right] < a[right+1]:
            while left >= 0 and a[left] >= a[right]:
                left -= 1
            res += left + 2
            right -= 1
        return res 

2.5 多指针滑动窗口

题目50930. 和相同的二元子数组

解题思路:多指针滑动窗口方法比较麻烦,暂时不考虑。

C++代码如下,

class Solution {
public:
    int numSubarraysWithSum(vector<int>& nums, int goal) {
        int n = nums.size();
        int s = 0;
        unordered_map<int,int> cnt;
        int res = 0;
        for (auto x : nums) {
            cnt[s] += 1;
            s += x;
            res += cnt[s-goal];
        }
        return res;
    }
};

python3代码如下,

前缀和+哈希表

class Solution:
    def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
        #哈希表
        s = 0
        res = 0
        cnt = collections.defaultdict(int)
        for x in nums:
            cnt[s] += 1 
            s += x 
            res += cnt[s-goal]
        return res 

分类讨论+模拟法,

class Solution:
    def numSubarraysWithSum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        a = []
        for i in range(n):
            if nums[i] == 1:
                a.append(i)
        if len(a) < k: #特判1的数目不足k个
            return 0 
        if k == 0: #特判k为0
            res = 0
            if len(a) > 0:
                t = a[0]
                res += t * (t + 1) // 2
                t = n-a[-1]-1
                res += t * (t + 1) // 2
                for i in range(len(a)-1):
                    t = a[i+1]-a[i]-1
                    res += t * (t + 1) // 2 
            else:
                res = n * (n + 1) // 2
            return res 
        res = 0
        for i in range(0,len(a)-k+1):
            #t1 = a[i] - a[i-1]
            t1 = a[i]
            if i-1 >= 0:
                t1 = t1 - a[i-1]
            else:
                t1 = t1 + 1
            #t2 = a[i+k] - a[i+k-1]
            t2 = -a[i+k-1]
            if i+k < len(a):
                t2 = a[i+k] + t2 
            else:
                t2 = n + t2 
            res += t1 * t2 
        return res 

题目511248. 统计「优美子数组」

解题思路:01数组,区间内1的数组为k,求这样的子区间数目。套路题。前缀和+哈希表。

C++代码如下,

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> a(n, 0);
        for (int i = 0; i < n; ++i) {
            if (nums[i] % 2 == 1) {
                a[i] = 1;
            }
        }

        int res = 0;
        unordered_map<int, int> cnt;
        int s = 0;
        for (auto x : a) {
            cnt[s] += 1;
            s += x;
            res += cnt[s-k];
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numberOfSubarrays(self, nums: List[int], k: int) -> int:
        n = len(nums)
        a = [0] * n 
        for i in range(n):
            if nums[i] % 2 == 1:
                a[i] = 1 
        
        s = 0
        cnt = collections.defaultdict(int)
        res = 0
        for x in a:
            cnt[s] += 1
            s += x 
            res += cnt[s-k]
        return res 

题目522563. 统计公平数对的数目

解题思路:二分查找。

C++代码如下,

class Solution {
public:
    long long countFairPairs(vector<int>& nums, int lower, int upper) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            //nums[i]
            //lower-nums[i] <= nums[j] <= upper-nums[i]
            //(1)找到一个数,它 >= lower-nums[i]
            auto it = lower_bound(nums.begin(), nums.end(), lower-nums[i]);
            int a = distance(nums.begin(), it);
            //(2)找到一个数,它 <= upper-nums[i]
            it = upper_bound(nums.begin(), nums.end(), upper-nums[i]);
            int b = distance(nums.begin(), it) - 1;
            if (a <= b) {
                res += b - a + 1;
                if (a <= i && i <= b) {
                    res -= 1; //去掉自身
                }
            } 
        }
        res /= 2; //去掉(i,j)和(j,i)的影响
        return res; 
    }
};

python3代码如下,

class Solution:
    def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int:
        nums.sort()
        n = len(nums)
        res = 0
        for i in range(n):
            #nums[i]
            #lower-nums[i] <= nums[j] <= upper-nums[i]
            a = bisect.bisect_left(nums, lower-nums[i]) #找到一个数,它>=lower-nums[i]
            b = bisect.bisect_right(nums, upper-nums[i]) #找到一个数,它<=upper-nums[i]
            b -= 1
            if a <= b:
                res += b-a+1
                if a <= i <= b:
                    res -= 1
        return res // 2

题目531712. 将数组分成三个子数组的方案数

解题思路如下:三指针滑动窗口。

C++代码如下,计算前缀和,区间[0,i]的元素之和为s[i+1]。用lr划分三个区间如下,

  1. left = [0, l),它的区间和为s[l]
  2. mid = [l, r),它的区间和为s[r] - s[l]
  3. right = [r, n),它的区间和为s[n] - s[r] 由上可知,l[l,r)l\in [l,r)r[2,n)r\in[2,n)

根据题目要求,可知,

  1. left的区间和小于等于mid的区间和,即s[l] <= s[r] - s[r],整理可得,s[l] <= s[r] // 2
  2. mid的区间和小于等于right的区间和,即s[r] - s[l] <= s[n] - s[r],整理可得,s[l] >= 2 * s[r] - s[n]

对于固定一个r,显然l越小,越容易满足条件1。很容易得知,存在一个数l1,当l取小于它的值时(即0 <= l < l1),满足条件1。 对于固定一个r,显然l越大,越容易满足条件2。很容易得知,存在一个数l2,当l取大于等于它的值时(即l2 <= l < n),满足条件2。

按照题意,条件1和条件2同时满足,才会有可行解,解的范围为l2 <= l < l1,因此,if l2 <= l1: ans += l1 - l2

class Solution {
public:
    int waysToSplit(vector<int>& nums) {
        int n = nums.size();
        vector<int> s(n+1, 0);
        for (int i = 1; i < n+1; ++i) {
            s[i] = s[i-1] + nums[i-1];
        }

        int right = 2;
        int l1 = 1, l2 = 1;
        int res = 0;
        const int mod = 1e9 + 7;
        while (right < n) {
            while (l1 < right && s[l1] <= s[right] - s[l1]) {
                l1 += 1;
            }
            while (l2 < l1 && s[right] - s[l2] > s[n] - s[right]) {
                l2 += 1;
            }
            if (l2 <= l1) {
                res += (l1 - l2) % mod;
                res %= mod;
            }

            right += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def waysToSplit(self, nums: List[int]) -> int:
        n = len(nums)
        s = [0] * (n+1)
        for i in range(1,n+1):
            s[i] = s[i-1] + nums[i-1] 

        right = 2
        l1 = 1
        l2 = 1
        res = 0
        while right < n:
            while l1 < right and s[l1] <= s[right]-s[l1]:
                l1 += 1 
            while l2 < l1 and s[right] - s[l2] > s[n] - s[right]:
                l2 += 1
            if l2 <= l1:
                res += l1 - l2 
            right += 1
        return res % (10 ** 9 + 7)

题目542444. 统计定界子数组的数目

解题思路:计算数组nums上一次元素值等于minK的下标idx1,计算数组nums上一次元素值等于maxK的下标idx2,计算数组nums上一次元素不在[minK,maxK]的下标idx3

C++代码如下,

class Solution {
public:
    long long countSubarrays(vector<int>& nums, int minK, int maxK) {
        long long res = 0;
        int n = nums.size();
        int idx1 = -1, idx2 = -1, idx3 = -1;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == minK) idx1 = i;
            if (nums[i] == maxK) idx2 = i;
            if (nums[i] < minK || nums[i] > maxK) idx3 = i;
            res += max(min(idx1,idx2)-idx3, 0);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def countSubarrays(self, nums: List[int], minK: int, maxK: int) -> int:
        n = len(nums)
        idx1 = idx2 = idx3 = -1
        res = 0
        for i,x in enumerate(nums):
            if x == minK:
                idx1 = i 
            if x == maxK:
                idx2 = i 
            if not minK <= x <= maxK:
                idx3 = i 
            res += max(min(idx1,idx2)-idx3,0)
        return res 

题目55992. K 个不同整数的子数组

解题思路:由题意可知,固定right,随着left的增加,区间内不同数应该是先大于k,然后等于k,再然后小于k。因此,设首次等于k的下标为idx1,首次小于k的下标为idx2,当left[idx1,idx2)的值,均满足条件。

C++代码如下,

class Solution {
public:
    int subarraysWithKDistinct(vector<int>& nums, int k) {
        int res = 0;
        int n = nums.size();
        map<int, int> cnt1, cnt2;
        int tot1 = 0, tot2 = 0;
        int idx1 = 0, idx2 = 0;
        for (int i = 0; i < n; ++i) {
            cnt1[nums[i]] += 1;
            if (cnt1[nums[i]] == 1) tot1 += 1;
            cnt2[nums[i]] += 1;
            if (cnt2[nums[i]] == 1) tot2 += 1;
            while (tot1 > k) {
                cnt1[nums[idx1]] -= 1;
                if (cnt1[nums[idx1]] == 0) {
                    tot1 -= 1;
                }
                idx1 += 1;
            }
            while (tot2 > k-1) {
                cnt2[nums[idx2]] -= 1;
                if (cnt2[nums[idx2]] == 0) {
                    tot2 -= 1;
                }
                idx2 += 1;
            }
            res += idx2 - idx1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def subarraysWithKDistinct(self, nums: List[int], k: int) -> int:
        n = len(nums)
        cnt1 = collections.defaultdict(int)
        tot1 = 0
        cnt2 = collections.defaultdict(int)
        tot2 = 0 
        idx1 = idx2 = 0
        res = 0
        for i,x in enumerate(nums):
            cnt1[x] += 1
            if cnt1[x] == 1:
                tot1 += 1
            cnt2[x] += 1
            if cnt2[x] == 1:
                tot2 += 1
            while tot1 > k:
                cnt1[nums[idx1]] -= 1
                if cnt1[nums[idx1]] == 0:
                    tot1 -= 1
                idx1 += 1
            while tot2 > k-1:
                cnt2[nums[idx2]] -= 1
                if cnt2[nums[idx2]] == 0:
                    tot2 -= 1
                idx2 += 1 
            res += idx2 - idx1 
        return res 

3 参考

灵神力扣题单汇总