1 介绍
本博客用来记录灵神力扣题单之滑动窗口。
2 训练
2.1 定长滑动窗口
解题思路:维护一个长度为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
解题思路:模拟。
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
题目3:1984. 学生分数的最小差值
解题思路如下:模拟。
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
题目4:643. 子数组最大平均数 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
题目5:1343. 大小为 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
解题思路:模拟。
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
解题思路:模拟。
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
题目8:1052. 爱生气的书店老板
解题思路:模拟。
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
解题思路:模拟。注意滑动区间内次数由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
题目10:2461. 长度为 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
题目11:1423. 可获得的最大点数
解题思路:模拟。
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
解题思路:连续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
题目13:2653. 滑动子数组的美丽值
解题思路:固定区间长度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
题目14:567. 字符串的排列
解题思路:模拟。
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
题目15:438. 找到字符串中所有字母异位词
解题思路:小写字母,模拟。
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
题目16:2156. 查找给定哈希值的子串
解题思路:除法不好进行取余操作,尽量转换成加法、减法和乘法,迫不得已的情况下,才考虑乘法逆元。快速幂算法需牢记。减号取模,最好将结果值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]
题目17:2953. 统计完全子字符串
解题思路:首先依据相邻字符位置不超过2,将原字符串s
切片成多个子串,对于每个子串进行如下处理。考虑只有一个字符出现k
次,那么滑动区间长度为k
,cnt = 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 不定长度的滑动窗口(求最大值)
题目18:3. 无重复字符的最长子串
解题思路:模拟。注意更新滑动区间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
题目19:1493. 删掉一个元素以后全为 1 的最长子数组
解题思路:把值为0的下标存储起来。遍历每一个下标,计数leftcnt
和rightcnt
。
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
题目20:2730. 找到最长的半重复子字符串
解题思路:先找到满足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
题目21:904. 水果成篮
解题思路:每次更新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
题目22:1695. 删除子数组的最大得分
解题思路:记滑动窗口的左右端点分别为i
和j
,有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
不定长度的滑动区间算法经典题目!!!注意,一般该类题型的假设条件是区间长度不能过长,比如此题中的“最多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
题目24:2024. 考试的最大困扰度
解题思路:考虑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
题目25:1004. 最大连续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
题目26:2401. 最长优雅子数组
解题思路:注意滑动区间左端点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
解题思路:注意滑动区间左端点left
的更新,以及滑动区间要包含原数组i=0
或i=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
题目28:1838. 最高频元素的频数
解题思路:注意滑动区间左端点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
题目29:2516. 每种字符至少取 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
题目30:2831. 找出最长等值子数组
解题思路:先处理出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
题目31:2106. 摘水果
解题思路:如何想到更新滑动窗口左端点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
题目32:1610. 可见点的最大数目
解题思路:应用题。模拟+滑动窗口。
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
题目33:2781. 最长合法子字符串的长度
解题思路:注意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
题目34:2968. 执行操作使频率分数最大
解题思路:中位数贪心,即使得滑动区间[left,right]
内分数最大的x
为a[(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
解题思路:先枚举滑动窗口内出现的字符种类数目,然后再枚举滑动窗口的左右端点left
和right
。
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
题目36:1763. 最长的美好子字符串
解题思路:数据量小,模拟即可。
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 不定长度的滑动窗口(求最小值)
题目37:209. 长度最小的子数组
解题思路:滑动窗口套路题,注意更新滑动窗口的左端点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
题目38:1234. 替换子串得到平衡字符串
解题思路:满足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
解题思路:先枚举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
题目40:76. 最小覆盖子串
解题思路:滑动窗口算法。
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 不定长滑动窗口(求子数组个数)
题目42:2799. 统计完全子数组的数目
解题思路:套路题。
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
题目43:713. 乘积小于 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
解题思路:滑动窗口套路题。
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
解题思路:滑动窗口套路题。
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
解题思路:滑动窗口套路题。
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
题目47:2537. 统计好子数组的数目
解题思路:滑动窗口+组合数公式。
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
题目48:2762. 不间断子数组
解题思路:滑动窗口套路题。
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
解题思路:先考虑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 多指针滑动窗口
题目50:930. 和相同的二元子数组
解题思路:多指针滑动窗口方法比较麻烦,暂时不考虑。
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
题目51:1248. 统计「优美子数组」
解题思路: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
题目52:2563. 统计公平数对的数目
解题思路:二分查找。
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
题目53:1712. 将数组分成三个子数组的方案数
解题思路如下:三指针滑动窗口。
C++代码如下,计算前缀和,区间[0,i]
的元素之和为s[i+1]
。用l
和r
划分三个区间如下,
left = [0, l)
,它的区间和为s[l]
mid = [l, r)
,它的区间和为s[r] - s[l]
right = [r, n)
,它的区间和为s[n] - s[r]
由上可知,,。
根据题目要求,可知,
- left的区间和小于等于mid的区间和,即
s[l] <= s[r] - s[r]
,整理可得,s[l] <= s[r] // 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)
题目54:2444. 统计定界子数组的数目
解题思路:计算数组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
题目55:992. 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