Day5--滑动窗口与双指针--3. 无重复字符的最长子串,3090. 每个字符最多出现两次的最长子字符串,1493. 删掉一个元素以后全为 1 的最长子数组

Day5–滑动窗口与双指针–3. 无重复字符的最长子串,3090. 每个字符最多出现两次的最长子字符串,1493. 删掉一个元素以后全为 1 的最长子数组

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

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

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

今天的题目类型是:求最长子数组。

3. 无重复字符的最长子串

思路:

  1. 利用map记录窗口内元素个数。要一直保持窗口内元素数量都是1
  2. for循环遍历右指针
  3. 右指针元素进窗
    • 如果右指针元素数量大于1,证明窗口内有重复元素,且该元素就是右指针所处位置的元素
    • 左指针元素出窗,直到右指针元素数量变为1,此时窗口内无重复元素
    • 更新最大值
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] ch = s.toCharArray();
        int n = ch.length;

        // ASCII码有128个
        int[] map = new int[128];
        int len = 0;
        int maxLen = 0;

        // 窗口左指针
        int left = 0;

        // 开始滑动窗口,for循环遍历右指针
        for (int i = 0; i < n; i++) {
            char c = ch[i];

            // 右指针(i)入窗,如果数量为1,即为新元素,len++
            map[c]++;
            // 当右指针处的元素数量大于1,证明窗口内有重复元素
            // 循环直到右指针元素数量为1,这样能保证窗口内元素一直是互不相同的
            while (map[c] > 1) {
                // 左指针元素出窗
                map[ch[left]]--;
                // 收缩左指针
                left++;
            }
            // 更新长度
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

思路:

简洁版

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] ch = s.toCharArray();
        int n = ch.length;
        int[] map = new int[128];
        int maxLen = 0;
        int left = 0;
        
        for (int i = 0; i < n; i++) {
            char c = ch[i];
            map[c]++;
            while (map[c] > 1) {
                map[ch[left]]--;
                left++;
            }
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

思路:

自己第一次写的版本,有点累赘。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] ch = s.toCharArray();
        int n = ch.length;
        // ASCII码有128个
        int[] map = new int[128];
        int len = 0;
        int maxLen = 0;

        // 窗口左指针
        int left = 0;
        // 开始滑动窗口,for循环遍历右指针
        for (int i = 0; i < n; i++) {
            // 右指针(i)入窗,如果数量为1,即为新元素,len++
            if (++map[ch[i]] == 1) {
                len++;
                if (len > maxLen) {
                    maxLen = len;
                }
            } else {
                // 如果是旧元素,不算缩小窗口,left++
                // 直到右指针的元素数量为1,这样能保证窗口内元素一直是互不相同的
                while (map[ch[i]] > 1) {
                    map[ch[left++]]--;
                }
            }
            // 更新目前窗口的长度(右-左+1)
            len = i - left + 1;
        }
        return maxLen;
    }
}

3090. 每个字符最多出现两次的最长子字符串

和上题一模一样,上题是最多出现一次,这题是最多出现两次。(但是这题反而难度是简单题,搞笑了)

思路:

同上,仅需把while (map[c] > 1)改成2.

class Solution {
    public int maximumLengthSubstring(String s) {
        char[] ch = s.toCharArray();
        int n = ch.length;
        int[] map = new int[128];
        int maxLen = 0;
        int left = 0;
        
        for (int i = 0; i < n; i++) {
            char c = ch[i];
            map[c]++;
            while (map[c] > 2) {
                map[ch[left]]--;
                left++;
            }
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

这里可以开长度为26的数组,然后map[c-‘a’],语义更清晰了,但是写得更慢了。

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

思路【我】:

  1. 题目含义:窗口里面最多只能有一个0
  2. 不定长滑动窗口三步曲:入–出–更新
    1. 入:右指针进窗
    2. 出:当zero不满足窗口要求,左指针收缩
    3. 更新
  3. 结果:题目要求的是,删除0之后最长为1的子数组长度。因为我们默许窗口内最多有一个0,所以要减一
class Solution {
    public int longestSubarray(int[] nums) {
        // 窗口里面最多只能有一个0
        int n = nums.length;
        int maxLen = 0;
        int zero = 0;
        int left = 0;
        for (int i = 0; i < n; i++) {
            // 右指针进窗
            if (nums[i] == 0) {
                zero++;
            }
            // 当zero不满足窗口要求,左指针收缩
            while (zero > 1) {
                // 当且仅当左指针元素为0,才zero--
                if (nums[left] == 0) {
                    zero--;
                }
                left++;
            }
            // 更新
            maxLen = Math.max(maxLen, i - left + 1);
        }
        // 题目要求的是,删除0之后最长为1的子数组长度。因为我们默许窗口内最多有一个0,所以要减一
        return maxLen - 1;
    }
}

思路【我】:

纯净版:

class Solution {
    public int longestSubarray(int[] nums) {
        int n = nums.length;
        int maxLen = 0;
        int zero = 0;
        int left = 0;
        for (int i = 0; i < n; i++) {
            if (nums[i] == 0) {
                zero++;
            }
            while (zero > 1) {
                if (nums[left] == 0) {
                    zero--;
                }
                left++;
            }
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen - 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值