- 综述
 - 模板
 - 例题
- 1176. 健身计划评估">【求和】1176. 健身计划评估
 - 1456. 定长子串中元音的最大数目">【计数】1456. 定长子串中元音的最大数目
 - 643. 子数组最大平均数 I">【平均数】643. 子数组最大平均数 I
 - 1052. 爱生气的书店老板">【有条件计算】1052. 爱生气的书店老板
 - 1423. 可获得的最大点数">【左右两端】1423. 可获得的最大点数
 - 438. 找到字符串中所有字母异位词">【排列】438. 找到字符串中所有字母异位词
 - 567. 字符串的排列">【排列】567. 字符串的排列
 - 1151. 最少交换次数来组合所有的 1">【交换/中等】1151. 最少交换次数来组合所有的 1
 
 
综述
通常分为两段式,先计算一个定长窗口,然后再向右依次滑动窗口,每滑动一次窗口,更新一次结果集。
模板
写法一:用两个指针l, rlet l = 0, r = 0, ans = 结果集while(r < k) {计算窗口r++}while( r < |len|) {滑动窗口,加入新的r元素,去除旧的l元素,r++,l++;更新结果集}return ans;写法二:只使用一个主指针r, 由于是定长,所以可以直接计算l的位置。let r = 0, ans = 结果集while(r < k) {计算窗口r++}while( r < |len|) {滑动窗口,加入新的r元素,去除旧的l元素,r++,l = r - k;更新结果集}return ans;
例题
【求和】1176. 健身计划评估
难度简单17收藏分享切换为英文接收动态反馈
你的好友是一位健身爱好者。前段日子,他给自己制定了一份健身计划。现在想请你帮他评估一下这份计划是否合理。
他会有一份计划消耗的卡路里表,其中 calories[i] 给出了你的这位好友在第 i 天需要消耗的卡路里总量。
为了更好地评估这份计划,对于卡路里表中的每一天,你都需要计算他 「这一天以及之后的连续几天」 (共 k 天)内消耗的总卡路里 T:
- 如果 
T < lower,那么这份计划相对糟糕,并失去 1 分; - 如果 
T > upper,那么这份计划相对优秀,并获得 1 分; - 否则,这份计划普普通通,分值不做变动。
 
请返回统计完所有 calories.length 天后得到的总分作为评估结果。
注意:总分可能是负数。
/*** @param {number[]} calories* @param {number} k* @param {number} lower* @param {number} upper* @return {number}*/var dietPlanPerformance = function(calories, k, lower, upper) {let t = 0, r = 0, ans = 0;while (r < calories.length && r < k) {t += calories[r++];}const update = (t) => {if (t > upper) {return 1;}if (t < lower) {return -1;}return 0;};ans += update(t);while (r < calories.length) {t += calories[r];t -= calories[r - k];ans += update(t);r++;}return ans;};
【计数】1456. 定长子串中元音的最大数目
/*** @param {string} s* @param {number} k* @return {number}*/var maxVowels = function(s, k) {const dict = new Set(['a', 'e', 'i', 'o', 'u']);let t = 0;for (let i = 0; i < k; i++) {t += dict.has(s.charAt(i)) ? 1 : 0;}let ans = t;for (let i = k; i < s.length; i++) {t += dict.has(s.charAt(i)) ? 1 : 0;t -= dict.has(s.charAt(i - k)) ? 1 : 0;ans = Math.max(ans, t);}return ans;};
【平均数】643. 子数组最大平均数 I
/*** @param {number[]} nums* @param {number} k* @return {number}*/var findMaxAverage = function(nums, k) {let ans = 0, t = 0;for (let i = 0; i< k; i++) {t+= nums[i];}ans = t;for (let i = k; i < nums.length; i++) {t = t + nums[i] - nums[i-k];ans = Math.max(ans, t);}return ans/k;};
【有条件计算】1052. 爱生气的书店老板
// 写法一:定长式简单写法,不死脑细胞/*** @param {number[]} customers* @param {number[]} grumpy* @param {number} minutes* @return {number}*/var maxSatisfied = function(customers, grumpy, minutes) {let ans = 0, t = 0, base = 0;for (let i = 0; i < minutes; i++) {t += customers[i] * grumpy[i];base += customers[i] * (1 - grumpy[i]);}ans = t;for (let i = minutes; i < customers.length; i++) {base += customers[i] * (1 - grumpy[i]);t += customers[i] * grumpy[i];t -= customers[i - minutes] * grumpy[i-minutes]ans = Math.max(ans, t);}return base + ans;};/*** @param {number[]} customers* @param {number[]} grumpy* @param {number} minutes* @return {number}*/var maxSatisfied = function(customers, grumpy, minutes) {let sum = 0, start = 0, base = 0, maxDiff = 0;// 求的结果为最多满意的客户,由于可以作弊变脸色,所以结果可以分成两个部分,一个是不受变脸色影响的结果,这个部分的特点是grumpy[i] 为0;// 受影响是grumpy[i] === 1的部分,而minutes为窗口大小,故只需要计算grumpy[i] === 1 时的customers在窗口中的最大窗口值。for (let i = 0; i < customers.length; i++) {// 不受影响的部分的前缀和计算base += customers[i] * ( 1 - grumpy[i]);// 受影响的部分的前缀和计算sum += customers[i] * grumpy[i];// 计算影响因子的大小,求最大值。最大值一般产生在窗口扩展时maxDiff = Math.max(maxDiff, sum);// 窗口大小满足,为固定窗口,所以不用循环,只判断一次。if (i - start + 1 === minutes) {// 窗口右移,计算区间和sum -= customers[start] * grumpy[start];// 如果需要计算最小值,可以在在次计算。发生在窗口收缩// 窗口右移start++;}}// 两部分之和。return base + maxDiff;};// 写法三:不定长的方式来写。/*** @param {number[]} customers* @param {number[]} grumpy* @param {number} minutes* @return {number}*/var maxSatisfied = function(customers, grumpy, minutes) {let l = 0, r = 0, ans = 0, t = 0, base = 0;while (r < customers.length) {base += customers[r] * (1 - grumpy[r]);t += customers[r] * grumpy[r];r++;while (r - l > minutes) {t -= customers[l] * grumpy[l];l++;}ans = Math.max(ans, t);}return base + ans;};
【左右两端】1423. 可获得的最大点数
/*** @param {number[]} cardPoints* @param {number} k* @return {number}*/var maxScore = function(cardPoints, k) {let ans = 0, t = 0, len = cardPoints.length;for (let i = len - k ; i <len; i++) {t += cardPoints[i];}ans = t;for (let i = 0; i < k; i++) {t += cardPoints[i] - cardPoints[len - k + i];ans = Math.max(ans, t);}return ans;};
【排列】438. 找到字符串中所有字母异位词
/*** @param {string} s* @param {string} p* @return {number[]}*/var findAnagrams = function(s, p) {let ct = new Array(26).fill(0), ans=[], base = 'a'.charCodeAt(0);const t = new Array(26).fill(0);for (let r = 0; r < p.length; r++) {ct[p.charCodeAt(r) - base]++;t[s.charCodeAt(r) - base]++;}const isValidWindow = () => {for (let i = 0; i < ct.length; i++) {if (ct[i] === 0) continue;if (ct[i] !== t[i]) return false;}return true;};const updateAns = (pos) => {if (isValidWindow()) {ans.push(pos);}};updateAns(0);for (let r = p.length; r < s.length; r++) {t[s.charCodeAt(r) - base]++;t[s.charCodeAt(r - p.length) - base]--;updateAns(r - p.length + 1);}return ans;};
【排列】567. 字符串的排列
这题可以看作是上面的题目 438的简化,只需要判断是否有,而不用全记录, 直接基于上题的模板套路修改下结果更新。
/*** @param {string} s1* @param {string} s2* @return {boolean}*/var checkInclusion = function(s1, s2) {let s = s2, p = s1;let ct = new Array(26).fill(0), base = 'a'.charCodeAt(0);const t = new Array(26).fill(0);for (let r = 0; r < p.length; r++) {ct[p.charCodeAt(r) - base]++;t[s.charCodeAt(r) - base]++;}const isValidWindow = () => {for (let i = 0; i < ct.length; i++) {if (ct[i] === 0) continue;if (ct[i] !== t[i]) return false;}return true;};if (isValidWindow()) {return true;}for (let r = p.length; r < s.length; r++) {t[s.charCodeAt(r) - base]++;t[s.charCodeAt(r - p.length) - base]--;if (isValidWindow()) {return true;}}return false;};
【交换/中等】1151. 最少交换次数来组合所有的 1
给出一个二进制数组 data,你需要通过交换位置,将数组中 任何位置 上的 1 组合到一起,并返回所有可能中所需 最少的交换次数。
思路:
目标是,是将所有的11集中在一起,这个时候的窗口长度为真个串中1的个数len,在这种情况下,要使交换次数最少,即为其中的0的数量最少。
于是将原问题转换为求长度为len的窗口中,0最少的窗口,而答案即为窗口内0的数量。
由于只有0、1两种数字,个数的计算等价与求和,故计算窗口的sum值,最后再以窗口长度-sum即可。
/*** @param {number[]} data* @return {number}*/var minSwaps = function(data) {// 最少交换,存在一个长度为 |1的个数| 的窗口,计数其中的1的个数为最大,反过来这个区间中的0的个数即为最少交换次数。let t = data.filter(p => p === 1).length;let r = 0, s = 0, ans = 0;while (r < t) {s += data[r++];}ans = s;while (r < data.length) {s += data[r];s -= data[r-t];ans = Math.max(ans, s);r++;}return t - ans;};
