【数据结构与算法】力扣 3. 无重复字符的最长子串

题目描述

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

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

分析解答

一眼看到 重复字符串数组 这样的关键词,首先应该想到的是 滑动窗口

然后我们需要保留已经扫描过了子串,所有还需要使用到 哈希表 Map

代码如下:

/**
 * 无重复字符的最长子串
 * @param s 原始字符串
 * @description
 * 双指针 -> 字符串、数组 都可用使用类似的解法
 * - right 从头扫描到尾部
 * - left 默认指向0,发现right出现了重复的字符时, left直接指向左边重复的下一个元素
 * - 保存已经扫描的字符 - 哈希表(Map: { key: value } key 为字符,value 为索引)
 * - 注意点:必须判断 map.get(s[right]) >= left 保证不会把滑动窗口之外的,但是是 Map 里的字符考虑在内
 */
function lengthOfLongestSubstring(s: string): number {
    let left = 0
    const map = new Map()
    let length = 0
    for (let right = 0; right < s.length; right++) {
        if (map.has(s[right]) && map.get(s[right]) >= left) {
            left = map.get(s[right]) + 1
        }
        map.set(s[right], right);
        length = Math.max(length, right - left + 1)
    }
    return length
};

思路拓展

很多人第一次写这道题都会有一个疑问:为什么判断条件里要有 map.get(s[right]) >= left,为什么不能只判断 map.has(s[right])


✅ 正确写法:

if (map.has(s[right]) && map.get(s[right]) >= left) {
    left = map.get(s[right]) + 1;
}

🤔 为什么不能只写 map.has(s[right])

假设字符串是:

s = "abba"

我们一步步来看:

rights[right]map 内容left说明
0‘a’{‘a’:0}0新字符,正常移动
1‘b’{‘a’:0,‘b’:1}0新字符,正常移动
2‘b’{‘a’:0,‘b’:2}0❌ or 2✅重复字符,但关键是:之前的 ‘b’ 位置(1)是不是在当前窗口里? 是的(1 >= left),要移动 left = 1 + 1 = 2
3‘a’{‘a’:3,‘b’:2}2‘a’ 虽然出现在 map 中,但它上次出现的位置 0 小于 left=2,已经不在窗口里,不能再移动 left ❌

💡 所以 map.get(s[right]) >= left 的作用是:

确保这个重复字符确实 在当前窗口中,而不是在窗口外。


❌ 错误写法(只有 map.has)的问题:

if (map.has(s[right])) {
    left = map.get(s[right]) + 1;
}

s = "abba" 的例子中,处理最后一个 'a' 时,会误以为 'a' 重复了,强行把 left2 移到 1,导致窗口“回退”,出错。


✅ 总结

必须加 && map.get(s[right]) >= left,是为了保证 map 中记录的重复字符位置是当前窗口里的,避免误判窗口外的旧字符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值