Day12–滑动窗口与双指针–2762. 不间断子数组,LCP 68. 美观的花束,2743. 计算没有重复字符的子字符串数量
今天要训练的题目类型是:【不定长滑动窗口】,题单来自@灵艾山茶府。
滑动窗口相当于在维护一个队列。右指针的移动可以视作入队,左指针的移动可以视作出队。
不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,求子数组个数。
今天的题目类型是:求子数组个数——求最长(越短越合法)
2762. 不间断子数组
思路【我】:
需要一个自动排序,且可以取首尾元素的数据结构。这里我们使用TreeMap。
三步曲:入,出,更新。
- 入。
map.merge(r, 1, Integer::sum);
- 出。当窗口内最大值-最小值大于2,出掉窗口最左元素。
map.merge(l, -1, Integer::sum)
- 如果减为0了,证明不存在这个元素,要记得移除,不能再参与比较了。
- 更新
count += i - left + 1;
class Solution {
public long continuousSubarrays(int[] nums) {
int n = nums.length;
// 需要一个自动排序,且可以取首尾元素的数据结构。
TreeMap<Integer, Integer> map = new TreeMap<>();
int left = 0;
long count = 0;
for (int i = 0; i < n; i++) {
// 1,入
int r = nums[i];
map.merge(r, 1, Integer::sum);
// 2,出。当窗口内最大值-最小值大于2.
while (map.lastKey() - map.firstKey() > 2) {
int l = nums[left];
// 如果减为0了,证明不存在这个元素,要记得移除,不能再参与比较了。
// merge()函数,返回值是:操作减一后,该key对应的value
if (map.merge(l, -1, Integer::sum) == 0) {
map.remove(l);
}
// 出
left++;
}
// 3,更新
count += i - left + 1;
}
return count;
}
}
LCP 68. 美观的花束
- 这种题目,窗口内,同种类型元素的数量不能超过cnt个,我们在定长滑动窗口,求最长子数组的时候,都已经做过了。
- 都是一样的三步曲,只不过出和更新的时机不同,要统计的值不同(有的求窗口大小,有的求子数组大小)。
- 现在是练习的时候,聚在一起了,我们一个套路解决一堆题目。但是到实际比赛的时候,要锻炼自己能分的出来是属于什么类型的,应该什么时候出窗,什么时候更新res。
思路【我】:
- 三部曲:入,出,更新。
- 入
- 出。当存在花朵数量超过cnt时。做标记
- 出掉最左元素。
- 如果此时该种花数量回到cnt,证明它是罪魁祸首,且已经从良了。标记取消。
- 更新
- 看清楚题目要求,要取模。最后返回的是int类型
class Solution {
public int beautifulBouquet(int[] flowers, int cnt) {
int n = flowers.length;
// 数组用作哈希表
int[] map = new int[100005];
int left = 0;
long count = 0;
int types = 0;
boolean notBeautiful = false;
for (int i = 0; i < n; i++) {
// 1,入
int r = flowers[i];
// 2,出。当存在花朵数量超过cnt时。做标记
if (++map[r] > cnt) {
notBeautiful = true;
}
while (notBeautiful && left < i) {
// 出掉最左元素。
// 如果此时该种花数量回到cnt,证明它是罪魁祸首,且已经从良了。标记取消。
if (--map[flowers[left]] == cnt) {
notBeautiful = false;
}
left++;
}
// 3,更新
count += i - left + 1;
}
// 看清楚题目要求,要取模。最后返回的是int类型
count %= 1000000007;
return (int) count;
}
}
2743. 计算没有重复字符的子字符串数量
思路【我】:
和上一题一模一样的思路。修改变量名,数据类型就可以提交了。
- 需要注意的是:虽然是小写字母,但我还是开了长度为128的数组作为map。
- 因为方便。如果开26长度的话,每次要减去’a’。而128长度是ASCII码长度,全包进去了。
- 如果每次,都需要遍历整个map的,map中非必要元素存在0的情况会影响结果的。这时候不适宜直接开128长度,甚至可能不适宜开26。转而用map处理。需要用到map.size()来知道窗口中有几种不同类型的。
class Solution {
public int numberOfSpecialSubstrings(String s) {
char[] ch = s.toCharArray();
int n = ch.length;
int[] map = new int[128];
int left = 0;
int count = 0;
int types = 0;
boolean notSpecial = false;
for (int i = 0; i < n; i++) {
// 1,入
char r = ch[i];
// 2,出.当某个字符数量大于1,说明存在重复字符,做标记.
if (++map[r] > 1) {
notSpecial = true;
}
while (notSpecial && left < i) {
// 出掉最左元素。
// 如果此时该字符数量回到1,证明它是罪魁祸首,且已经从良了。标记取消。
if (--map[ch[left]] == 1) {
notSpecial = false;
}
left++;
}
// 3,更新
count += i - left + 1;
}
return count;
}
}