力扣 每日刷题——给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度

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

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

解题思路1:效率稍微低一些

  • 先了解函数 string.indexOf(substring, start) 用于判断 string中start位置之后,substring第一次出现的位置,如果没有找到则返回-1
  • 先利用indexOf 来 判断字母是否是第一次出现在这个子串中,判断方法为:如果字母的index小于当前循环i,则代表这个字母在前面已经出现过了,因为第一次出现的位置比当前循环的位置小
  • 如果字母前面有存在重复的字母,则将minIndex移动到 重复字母的后一位 ,重新计算新的子串的长度,例如 abacd 则遇到第二个a的时候,则minIndex从b开始 计算新的子串
  • 如果是第一次出现,则在这个子串里面没有重复字母,可以直接计算长度,不重复长度为当前循环i - minIndex + 1,第0个字母的长度为1,而不是0,所以要加1
  • 由于每次循环的时候都把最大的长度保存在len值,如果拿到当前的最大长度i - minIndex + 1是大于之前保存的len,则Math.max()替换掉,保留不重复字符串的最大长度
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    // minIndex: 字符串某字母的最小index
    let minIndex = 0
    // len: 不重复字符串的最大长度
    let len = 0
    for(let i = 0; i < s.length; i++) {
        s.indexOf(s[i], minIndex) < i ? 
        minIndex = s.indexOf(s[i], minIndex) + 1 :
        len = Math.max(len, i - minIndex + 1)
    }
    return len
};

解题思路2: 双指针维护滑动窗口 高效一些

  • 整体思路就是左指针不动,右指针在没遇到重复字符时一直往右移动,如果遇到重复字符了就重新开始记录,即左指针移动到最近一个重复字符的右边,重新开始记录。

    滑动窗口用来记录最长不出现重复字符的子串的区间
    哈希表用来记录每个字符最后(最新)出现的索引

abba

right = 0,首先遇到 a,没重复,long 更新为 1,同时 map.set('a', 0);
right = 1,遇到 b,没重复,long 更新为 2,同时 map.set('b', 1);
right = 2,遇到 b,重复了,同时判定 b 上一次出现位置是 1,大于等于 left,所以对 left 进行更新,更新成 1
right = 3,遇到 a,重复了,同时判定 a 上一次出现位置是 0,此时注意,如果没有判断 0 是否大于		
等于 left 这一步 会发生什么?
1.如果直接让 left 更新成 a 上一次出现位置 0 再加1的话,此时 left 变成1,但很明显看出 [left, right) 区间即 [1, 3) 内有重复的 b,不符合!
2.所以得加上 map.get(s[right]) >= left 这个判定条件,右指针目前所指向的当前重复字符的上一次出现位置在左指针右边,保证滑动窗口左边界在遇到重复字符时一直能及时更新到最新状态,而不是用之
前的旧状态,而导致[left, right) 区间出现重复
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let left = 0;
    let long = 0;
    const map = new Map(), len = s.length;
    for(let right = 0; right < len; right++){
        // 遇到重复字符时还要判定 该重复字符的上一次出现位置是否在 滑动窗口左边界 left 的右边
        if(map.has(s[right]) && map.get(s[right]) >= left){
            left = map.get(s[right]) + 1; // 都满足,则更新,更新到最近出现的那个重复字符,它的上一个索引的右边
        }
        long = Math.max(long, right - left + 1); // 比较滑动窗口大小与 long 的长度
        map.set(s[right], right); // 无论有没有重复,每次遍历都要更新字符的出现位置
    }
    return long;
};

作者:smooth-b
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/jian-dan-shuang-zhi-zhen-wei-hu-hua-dong-atl5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值