【Warrior刷题笔记】力扣3. 无重复字符的最长子串 【记忆化滑动窗口+双指针+哈希】逐行注释

题目

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

示例 1:

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

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

输入: s = ""
输出: 0

思路和算法

本题可以使用双指针+记忆化滑动窗口+哈希表解决。
使用左右指针leftright分别标记滑动窗口的左右边界,使用计数器maxLenlen分别存储目前为止最长子串的长度和当前滑动窗口所表示的子串的长度,同时使用哈希表存储已经遍历过的字符,键为字符本身,值为字符在字符串中的下标。
对于每个当前滑动窗口,检查右边界right所指向的字符是否已经遍历过了。如果已经遍历过了,再检查其位置是否大于等于左边界left,也即,上一个与该字符相同的字符是否在当前滑动窗口中,或者说当前滑动窗口所表示的子串,是否包含重复字符:
1.如果已遍历过但不在当前滑动窗口中,则右指针加一,len值加一,同时更新该字符的哈希值为当前位置;
2.如果已遍历过且位置位于当前滑动窗口中,则更新maxLen,更新左指针的位置为该字符原哈希值加一,更新len值,更新该字符哈希值为新位置,右指针加一;
3.如果未遍历过,则新增该字符记录,滑动窗口长度加一,右指针加一。
至右指针到达字符串边界时,最后一次更新maxLen并返回。

代码

class Solution {//解决方案类
    //因判例数量巨大,使用静态变量节省空间
    static int m;//存储字符串长度,避免反复调用s.size()造成较大开销
    static int maxLen;//存储答案
    static int left;//左指针
    static int right;//右指针
    static int len;//记录当前滑动窗口长度
    static unordered_map<char,int> map;//记录已遍历字符
public://公有成员
    int lengthOfLongestSubstring(string s) {//寻找字符串s中的最长不重复子串长度
        m = s.size();//字符串长度
        maxLen = 0;//答案初始化为零
        left = 0;//左指针初始化为零
        right = 0;//右指针初始化为零
        len = 0;//当前滑动窗口长度初始化为零
        map.clear();//字符串哈希map初始化清空
        while(right<m){//当右指针不为m
            if(map.count(s[right])){//判断右指针指向的当前字符是否出现过
                if(map[s[right]]<left){//如果确实出现过但是位置处于左指针左边,不在滑动窗口中
                    len++;//则滑动窗口长度加一
                    map[s[right]] = right;//同时更哈希map中该字符的位置
                    right++;//右指针加一扩大滑动窗口
                }
                else{//如果该字符已经出现过,且位于当前滑动窗口中
                    maxLen = max(maxLen,len);//则更新答案
                    //一般说来按常规理解应该是更新左指针变动滑动窗口才能更新当前滑动窗口长度,
                    //但是这里为了便于执行进行了相反的操作,不影响算法正确性
                    len -= map[s[right]]-left;//更新滑动窗口长度
                    left = map[s[right]]+1;//将左指针置于该字符原位置的右边一个位置
                    map[s[right]] = right;//更新该字符最新位置
                    right++;//右指针加一
                }
            }
            else{//如果该字符未曾出现过
                map[s[right]] = right;//则新增该字符记录
                len++;//滑动窗口长度加一
                right++;//右指针加一
            }
        }
        maxLen = max(maxLen,len);//最后一次更新答案
        return maxLen;//返回答案
    }
};
//因判例数量巨大,使用静态变量节省空间
//静态变量初始化
int Solution::m = 0;//初始化记录字符串长度的int变量
int Solution::maxLen = 0;//初始化存储答案的int变量
int Solution::left = 0;//初始化存储左指针的int变量
int Solution::right = 0;//初始化存储右指针的int变量
int Solution::len = 0;//初始化存储当前滑动窗口长度的int变量
unordered_map<char,int> Solution::map;//初始化存储已遍历字符及其最新位置的unordered_map<char,int>变量

复杂度分析

时间复杂度: O(n)。只需对字符串遍历一遍即可。
空间复杂度: O(n)。用于记录已遍历字符。

更多知识分享内容:
力扣个人主页https://leetcode-cn.com/profile/articles/
牛客个人主页https://blog.nowcoder.net/newcoderthewarrior
CSDN个人主页https://blog.csdn.net/qq_39255924

可可爱爱的蕾姆镇
蕾姆01.bmp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值