LeetCode学习笔记——最长子串(经典滑动窗口)

个人博客:The Blog Of WaiterXiaoYY 欢迎来互相交流学习。

Sliding Window

Sliding Window 也就是”滑动窗口“,通常用来求解一个字符串的字串。

滑动窗口一般配合哈希表来使用,

哈希表用来记录字符出现的次数,然后根据我们的需求的来处理这个字符。

为什么叫滑动窗口?

因为做题的方法感觉是一个一定长度的格子窗口,

比如像这样:

在这里插入图片描述

每个格子对应一个字符,然后设置判断条件,整个窗口一起移动,寻找我们所需要的东西。

但往往窗口的长度并不是固定的,我们在题目中慢慢体会。


无重复字符的最长字串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

题目不难理解,一个字符串s,在s中找出一个子字符串,在字符串中没有重复的字符,返回符合情况的最长子字符串的长度。

看到求子字符串,就应该想到滑动窗口。

那这道题,怎么利用滑动窗口来做呢?

以事例一为例说明:

我们需要两个指针,left 和 right ,表示窗口的左边界和右边界,

因为我们是去寻找最长的字串,所以我们的窗口应该从最小开始,慢慢变大,在窗口里面的就是我们要找的子串,

注意,也就是说,一旦纳入我们的窗口,就是符合条件的字串,如果字符重复就不能纳入进来,

我们需要利用哈希表来记录窗口里面字符出现的次数,

一开始,窗口的长度为0,左边界右边界都指向第一个数,

窗口内的字符是a,这时候哈希表 window 存入a出现的次数,

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时,我们纳入了第二个字符a,那这个窗口是不符合要求的,所以前面那个窗口就是我们要找的最大的窗口,

这时候我们需要记录前一个窗口大小,也就是子字符串的长度,

res = right - left,此时 res = 3,

但是此时的结果还不是最终的结果,窗口的右边界right,还没有达到字符串s的最右边,

说明字符串s还没有被遍历完,那就可能还有更长的情况,

但现在我们遇到一个问题,此时窗口的右边界不能移动了,因为重复的字符纳入到我们的窗口里面,我们的这个窗口不符合题目要求了,如果再移动右边界没有任何意义,

这时候我们应该移动左边界了,

左边界的应该移动到重复的那个字符串右边的第一个字符,这样我们的窗口才有继续扩大下去的条件,

在这里插入图片描述

此时,右边界才有移动下去的条件,那只需要再重复我们上面的操作就行,每次都记录下res,然后取最大的res返回即可。


如果明白了上面,我们就可以先来写写我们的伪代码:

String s;// 我们的字符串

此时left 和 right 都在最开始的地方
while(right < s.length()) {
	将右边界对应的字符存入到哈希表
    右边界增加 1
    如果此时遇到重复字符
    则进入移动左边界的循环
    while(window.get(i) > 1) {
        取出左边界的字符
        将左边界的字符从window中删去
        将左边界++
        直到将窗口内所有字符的个数都是 1
	}
    记录此时的窗口长度
    更新res
    return res;
}

如果明白了这些,我们的代码就可以很容易写出来,

有一点还需要注意的是,窗口长度是 right - left,而不是 right - left +1,

因为在每次记录字符的数量的同时,窗口右边界都指向了此时下一位,在我们将重复字符纳入窗口的那一刻,我们就记录下了字串的长度了。


代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
    	//建立哈希表window记录窗口中字符的数量
        HashMap<Character, Integer> window = new HashMap<>();
        //窗口的左右边界
        int left = 0;
        int right = 0;
        //记录我们的结果
        int res = 0;
        //右边界还没有到字符串s的右边界,说明res的结果还不是最终结果
        while(right < s.length()) {
        	//取出右边界的字符,将其记录在window中
            char c1 = s.charAt(right);
            window.put(c1, window.getOrDefault(c1,0) + 1);
            //记录字符的同时右边界也增加l
            right++;
            //遇到重复的字符,则开始移动左边界
            while(window.get(c1) > 1) {
                char c2 = s.charAt(left);
                int temp = window.get(c2);
                temp--;
                window.put(c2,temp);
                left++;
            }
            //取出窗口的最大值java
            res = Math.max(res, right - left);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值