不含重复字符的最长子字符串 | 循序递进---@二十一画

本文介绍了一种利用滑动窗口解决最长无重复字符子串问题的方法,并提供了两种实现方案的详细解析及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

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

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

提示:

  • 0 <= s.length <= 5 * 10^4
  • s 由英文字母、数字、符号和空格组成

分析:

相似题目:

拆解关键词:

【非重复字符、最长、连续字串】

优先考虑:

【滑动窗口】

想法:
滑动窗口V1:

本题要求寻找符合条件的连续子串,很容易想到使用滑动窗口(双指针)。来判断下,是否可以使用双指针:

  • 要求返回的答案下标是连续的吗? ✅
  • 每次窗口滑动是有界限的吗?不是那种必须滑动到最后一个下标才可以确定是否为解的吧?✅
  • 左右指针都是按照规律或者说是按照寻找解的要求来移动的吗?✅

滑动窗口是为了获取连续局部最优解,同时还不排除存在其他解的情况下。通过指针的移动来寻找每一个可能出现的解。最终得到全局最优解。

以上条件都满足,那么本题完全可以使用滑动窗口来做。

滑动窗口V2:

在移动left指针的时候,每次都是一个一个在移动,其实这里我们要找的就是元素不重复的那个left位置,本质就是要移动right指针,就要移除掉当前窗口内和right指针重复的那一个元素,以及那一个元素前面的全部元素[窗口内的]。

所以现在我们的目标就是定位到left应该在的位置,所以我们应该记录每一个元素出现的位置,这样就可以在下一次该元素再次出现的时候,顺利的移动left指针直接跳过这个元素。

因为要判断元素是否重复,所以我们还想要set的去重功能,但是这里我们还可以使用set吗?left指针要跳跃,那么set的元素在删除remove的时候,该怎么删除呢?所以,在这里我们不能使用set,如果使用set,那么left指针就不好跳跃,就算跳跃了,那set里面的元素也不能跳跃删除的。。

但是这里需要使用一个来记录元素的下标位置,这里使用map来记录每个元素的下标位置

这里尝试使用两个指针的位置来直接获取解。

代码:

滑动窗口V1:

image.png

class Solution {
    public int lengthOfLongestSubstring(String s) {

        //0、如果特殊情况不多的情况下,首先排除特殊情况
        if(s.length()<1)return 0; //如果没任何元素 那么返回0
        if(s.trim().length()<1) return 1; //如果有元素,但是都是空格,那么就返回1 因为空格也是一个元素

        //1、初始化
        int len = s.length();
        int left = 0;
        int right = 0;
        int maxLen = -1;
        /*如果判断是否出现重复元素  可以使用 HashSet  也可以使用 数组下标代值  这里我使用了set结构*/
        HashSet<Character> set = new HashSet<Character>();

        //2、窗口滑动 寻求解
        /**
        梳理什么时候right移动?什么时候left移动?
        ·如果当前窗口内没有重复元素(包含没有任何元素的情况,比如初始化)的时候,right++
        ·如果当前窗口发生了元素的重复,那么left++,直到当前不存在重复元素

        ·重复循环上述步骤,直到窗口大小超出范围
         */
        while(right<len){
            
            //如果set不包含新元素  那么一直添加新元素
            while(right<len && !set.contains(s.charAt(right))){
                set.add(s.charAt(right));
                right++;
            }
            //程序执行到这里,说明要不就是right超出范围,要不就是出现了重复元素

            maxLen = Math.max(right-left,maxLen);

            if(right==len) return maxLen; //如果此时right已经越界,那么说明从当前left位置开始后面的元素都不重复,可以直接返回结果了
            
            //如果不是越界,那么移动left指针,直到没有重复元素
            //循环操作:此时right指针对应的元素与set内元素冲突,为了不再冲突只能移除set集合里面和right对应元素相同的那个元素,因为要保持字串连续,所以从left到set内重复的元素的位置都要移除掉,直到可以让set添加新的元素
            while(left<=right && s.charAt(left)!=s.charAt(right)){
                set.remove(s.charAt(left));
                left++;
            }
            //此时left的位置恰好就是重复元素所在的位置  再移除掉这个元素  就可以添加新的元素了
            set.remove(s.charAt(left++));
        }

        return maxLen;
    }
}
滑动窗口V2:

image.png

class Solution {

    /**
    在移动left指针的时候,每次都是一个一个在移动,其实这里我们要找的就是元素不重复的那个left位置,本质就是要移动right指针,就要移除掉当前窗口内和right指针重复的那一个元素,以及那一个元素前面的全部元素[窗口内的]。

    所以现在我们的目标就是定位到left应该在的位置,所以我们应该记录每一个元素出现的位置,这样就可以在下一次该元素再次出现的时候,顺利的移动left指针直接跳过这个元素。

    因为要判断元素是否重复,所以我们还想要set的去重功能,但是这里我们还可以使用set吗?left指针要跳跃,那么set的元素在删除remove的时候,该怎么删除呢?所以,在这里我们不能使用set,如果使用set,那么left指针就不好跳跃,就算跳跃了,那set里面的元素也不能跳跃删除的。。

    但是这里需要使用一个来记录元素的下标位置,这里使用map来记录每个元素的下标位置

    这里尝试使用两个指针的位置来直接获取解。
     */

    public int lengthOfLongestSubstring(String s) {

        //0、如果特殊情况不多的情况下,首先排除特殊情况
        if(s.length()<1)return 0; //如果没任何元素 那么返回0
        if(s.trim().length()<1) return 1; //如果有元素,但是都是空格,那么就返回1 因为空格也是一个元素

        //1、初始化
        int len = s.length();
        int left = 0;
        int right = 0;
        int maxLen = -1;
        HashMap<Character,Integer> map = new HashMap<Character,Integer>();

        //2、开始循环
        while(right<len){

            //如果map不包含新元素  那么一直添加新元素  同时记录每个元素的下标位置
            while(right<len && !map.containsKey(s.charAt(right))){
                map.put(s.charAt(right),right);
                right++;
            }

            //程序执行到这里,说明要不就是right超出范围,要不就是出现了重复元素
            maxLen = Math.max(right-left,maxLen);
            if(right==len) return maxLen; //如果此时right已经越界,那么说明从当前left位置开始后面的元素都不重复,可以直接返回结果了

            //此时出现重复元素了,我们直接移动left 
            int pos = map.getOrDefault(s.charAt(right),0);
            left = Math.max(left,pos+1); //因为left可能之前移动过了,left不可以回退,所以我们取当前left和下标中的较大的那一个
            map.remove(s.charAt(right)); //既然已经跳过该位置,那么这个下标就可以清理了
        }
        return maxLen;

    }
}

总结:

使用滑动窗口的场景:

  • 要求返回的答案下标是连续的吗? ✅
  • 每次窗口滑动是有界限的吗?不是那种必须滑动到最后一个下标才可以确定是否为解的吧?✅
  • 左右指针都是按照规律或者说是按照寻找解的要求来移动的吗?✅

大家好,我是二十一画,感谢您的品读,如有帮助,不胜荣幸~😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值