3.无重复字符的最长子串(深度剖析解题代码)

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

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

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

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

输入: s = “”
输出: 0

提示:

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

使用双指针滑动窗口解题

hash 数组设置为 128 看一下 ascii 码表就懂了

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        // 索引是字符的ascii码,值是字符出现的次数
        int[] hash=new int[128];
        int left=0;
        int res=0;
        for(int right=0; right<s.length(); right++){
    
    
            // 一旦出现重复字符,广快移动左边界
            // 直到把重复的字符移出窗口(s.charAt(right)就是重复的字符)
            // 用来从hash表中取值的索引,要用字符的ascii码
            while(hash[s.charAt(right)]==1){
    
    
                hash[s.charAt(left)]--;
                left++;
            }
            hash[s.charAt(right)]++;
            res=Math.max(res, right-left+1);
        }
        return res;
    }
}

还可以优化

为什么要用 while 呢?

因为如果是 [b, c, a, e, d, a] 这种情况,当 right 指针走到第二个 a 时就会发现出现重复的字符了,因此要移动 left 指针到 下标为 3 的位置( e 的位置),从而窗口中不包含两个 a (窗口为 [left, right])

仔细想想,这不就是移动到上一个 a 的位置 +1 的位置吗,那我们直接移动 left 指针到这个位置不就好了吗?

因此我们只需要记录上一次该重复字符出现的位置就行了

用 hash 表来记录重复字符上一次出现的位置。

我们写出了第一版的代码

此方法暂时有 bug

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        // 记录字符上一次出现的位置
        int[] hash=new int[128];
        int left=0;
        int res=0;
        for(int right=0; right<s.length(); right++){
    
    
            // 不等于0了,说明出现了重复的字符
            if(hash[s.charAt(right)]!=0){
    
    
                left=hash[s.charAt(right)]+1;
            }
            hash[s.charAt(right)]=right;
            res=Math.max(res, right-left+1);
        }
        return res;
    }
}

bug 是什么呢?

我们这个 hash 表中值为 0 代表未出现,但是数组是从 0 开始的,因此下标为 0 的元素(也就是第一个元素),我们会误认为它没有出现过,比如 [a, b, c, a],这样就会多统计一个数。

那么让 hash 表初始值为 -1 就行了

第二版代码

此写法也有 bug

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        // 记录字符上一次出现的位置
        int[] hash=new int[128];
        Arrays.fill(hash, -1);
        int left=0;
        int res=0;
        for(int right=0; right<s.length(); right++){
    
    
            // 不等于-1了,说明出现了重复的字符
            if(hash[s.charAt(right)]!=-1){
    
    
                left=hash[s.charAt(right)]+1;
            }
            hash[s.charAt(right)]=right;
            res=Math.max(res, right-left+1);
        }
        return res;
    }
}

bug 是什么呢?

我们只是简单粗暴的把 left 指针移动到,上一次该字符出现的位置 +1 的位置,万一是 [a, b, b, a] 这种情况,left 指针本来都走到下标为 2 的位置,结果到最后一个字符时,就回退到 0 的位置。

解决 bug 的关键,就是让 left 指针不发生回退

第三版代码

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        // 记录字符上一次出现的位置
        int[] hash=new int[128];
        Arrays.fill(hash, -1);
        int left=0;
        int res=0;
        for(int right=0; right<s.length(); right++){
    
    
            // 不等于-1了,说明出现了重复的字符
            if(hash[s.charAt(right)]!=-1){
    
    
                // 很简单,取个max就可以不让left发生回退
                left=Math.max(left, hash[s.charAt(right)]+1);
            }
            hash[s.charAt(right)]=right;
            res=Math.max(res, right-left+1);
        }
        return res;
    }
}

这个 if 判断条件可以去掉,因为数组的每个索引都是大于等于 0 的

于是就出现了和热评第一相同的代码

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        // 记录字符上一次出现的位置
        int[] hash=new int[128];
        Arrays.fill(hash, -1);
        int left=0;
        int res=0;
        for(int right=0; right<s.length(); right++){
    
    
            left=Math.max(left, hash[s.charAt(right)]+1);
            hash[s.charAt(right)]=right;
            res=Math.max(res, right-left+1);
        }
        return res;
    }
}

最后的最后

其实第二种方法就是对滑动窗口 left 指针的移动进行了优化,left 不再一步一步走了,而是跳跃式的移动

有些代码虽然看上去很精简,很牛B,但其实它缺少了很多的细节,导致我们难以理解;编程语言是语言(搁这搁这呢),就像我们说话一样,如果我们一句话说的很精简,那么就可能缺失一些信息,导致别人难以理解。

所以并不是越精简的代码越好,信息全面,容易理解的代码才是最好的

Guess you like

Origin blog.csdn.net/lihuabeats/article/details/121725820