Day12--滑动窗口与双指针--2762. 不间断子数组,LCP 68. 美观的花束,2743. 计算没有重复字符的子字符串数量

Day12–滑动窗口与双指针–2762. 不间断子数组,LCP 68. 美观的花束,2743. 计算没有重复字符的子字符串数量

今天要训练的题目类型是:【不定长滑动窗口】,题单来自@灵艾山茶府

滑动窗口相当于在维护一个队列。右指针的移动可以视作入队,左指针的移动可以视作出队

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,求子数组个数。

今天的题目类型是:求子数组个数——求最长(越短越合法)

2762. 不间断子数组

思路【我】:

需要一个自动排序,且可以取首尾元素的数据结构。这里我们使用TreeMap。

三步曲:入,出,更新。

  1. 入。map.merge(r, 1, Integer::sum);
  2. 出。当窗口内最大值-最小值大于2,出掉窗口最左元素。map.merge(l, -1, Integer::sum)
    • 如果减为0了,证明不存在这个元素,要记得移除,不能再参与比较了。
  3. 更新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。

思路【我】:

  • 三部曲:入,出,更新。
    1. 出。当存在花朵数量超过cnt时。做标记
      • 出掉最左元素。
      • 如果此时该种花数量回到cnt,证明它是罪魁祸首,且已经从良了。标记取消。
    2. 更新
  • 看清楚题目要求,要取模。最后返回的是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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值