LeetCode 3. 无重复字符的最长子串 | 算法-从菜鸟开始

简介: 本文是《算法-从菜鸟开始》系列文章的第4篇,欢迎收藏、留言、点赞。话不多说,上题!

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


题目介绍:


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


示例:


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


注意:这道题目让输出的是不含有重复字符的最长子串的长度,而不是子串本身


二、解题思路


  1. 第一种解法:”老实人“又要上线了


题目中是要找不含有重复字符的子串,返回其长度。


第一时间我们可以想到找出所有的符合条件的子串,然后比较所有子串的长度,取最大的即可。


接下来就是考虑排列组合的问题。


在示例中,输入的是字符串”abca“,可以组成的排列组合有:


// 字符串abca可以组成的排列组合子串
// 字符a开始
ab | abc | abca
// 字符b开始
bc | bca
// 字符c开始
ca


再次筛选不包含重复字符串的:


ab | abc | bc | bca | ca


由此可得到结果,不含有重复字符的最长子串的长度是3


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
  // 记录最大子串长度
  let maxLen = 0;
  // 当前子串
  let currentSubStr = '';
  // 提供每一种组合
  for (let i = 0; i < s.length; i++) {
    // 每次遍历开始,初始化当前字符
    currentSubStr = s[i];
    // 开始排列组合
    for (let j = i + 1; j < s.length; j++) {
      // 每次判断当前字符是否已经存在于currentSubStr中
      // 如果存在说明当前的currentSubStr子串已经是目前最大的一个
      if (currentSubStr.includes(s[j])) {
        // 将当前子串的长度与历史记录的子串长度进行比较,取二者最大值
        maxLen = Math.max(currentSubStr.length, maxLen);
        // 将当前子串重置为当前字符,再次开始记录
        currentSubStr = s[j];
      } else {
        // 不重复字符,直接累积
        currentSubStr += s[j];
      }
    }
    // 最后一次比对
    maxLen = Math.max(currentSubStr.length, maxLen);
  }
  return maxLen;
}


执行测试用例:


网络异常,图片无法展示
|


OK,没问题我们就提交了!


网络异常,图片无法展示
|


执行结果是:超出时间限制,说明我们这个实现是不理想的,消耗时间太长。


分析下这个算法的时间复杂度: O(n2)O(n^2)O(n2)


在LeetCode的这个Case中,实际传入传入的字符串长度为 31000!


So,这不是一个好算法!


  1. 瞅瞅第二种解法:滑动窗口


滑动窗口是一个什么样的概念呢,大家可以想象为在横向平铺的字符串上,有一个可以滑动的框,框里面选中的是不重复的子串。


输入字符串:abcdbe,在遍历字符串时,我们使用数组subStr存储不重复的子串。
假定我们有两个指针:左指针和右指针,同时指向了不重复子串的首尾两个位置,在初始时都指向了字符串的第一个字符; 开始遍历时,右指针开始挪动,将不重复的字符依次放入到数组中。


如果遇到某个字符已经在数组subStr中,则说明当前不重复子串已经是最长了,需要开启新的不重复子串了。


新的不重复子串从哪里开始记录呢?!


重点: 从该重复字符在不重复子串中的下一个位置开始记录。


在该case中,第一次记录的不重复子串为 abcd。下一个字符b开始重复,新的一个不重复子串开始位置为c,即字符b第一次出现的位置的下一个字符。


此刻的处理方式是移动左指针位置,即将数组subStr中的元素从开头开始进行删除,直到移除重复字符b


将当前的字符b存储到subStr中。


比对当前不重复子串的长度与历史记录的最大长度,取最大值。


重复上述的过程,直到字符串遍历结束。


我们来看下代码上的实现


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
  // subStr存储不重复子串
  const subStr = [];
  // 记录不重复子串的最大值
  let maxLen = 0;
  // 遍历字符串s
  for (let i = 0; i< s.length; i++) {
    // 判断当前字符s[i]是否已经存在
    while (subStr.includes(s[i])) {
      // 如果存在,则移动指针,将该重复字符第一次出现的位置之前的所有字符清除,重新开始记录一个新的子串
      subStr.shift();
    }
    // 将当前字符存入
    subStr.push(s[i])
    // 比对当前子串的最大长度与历史记录最大长度,取最大值
    maxLen = Math.max(subStr.length, maxLen)
  }
  // 返回最大值
  return maxLen
};


此算法的时间复杂度是O(n)O(n)O(n)


执行代码,测试用例通过,提交算法,看看结果如何。


网络异常,图片无法展示
|


嗯,还是非常理想的。


三、总结


今天我们接触了一个非常重要的概念:滑动窗口,这是一个非常重要的概念和思维方式,希望各位小伙伴都能好好的理解一下,在后面的学习过程中我们还会多次碰到它。

从满足题目要求,到针对性能优化;从时间复杂度O(n2)O(n^2)O(n2)优化到时间复杂度O(n)O(n)O(n),算法总是一个探索、寻求最优解的过程。


除了使用滑动窗口这种方式之外,你还有其他的实现方式吗,欢迎留言交流~


算法-从菜鸟开始,而无止境。与君共勉!



相关文章
|
2月前
|
算法 前端开发 数据处理
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
48 0
|
2月前
|
存储 算法
Leetcode第三题(无重复字符的最长子串)
这篇文章介绍了解决LeetCode第三题“无重复字符的最长子串”的算法,使用滑动窗口技术来找出给定字符串中最长的不含重复字符的子串,并提供了详细的代码实现和解释。
77 0
Leetcode第三题(无重复字符的最长子串)
|
2月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
40 0
|
4月前
|
算法
【算法】位运算算法——判断字符是否唯一
【算法】位运算算法——判断字符是否唯一
|
4月前
|
算法
【算法】滑动窗口——无重复字符的最长子串
【算法】滑动窗口——无重复字符的最长子串
|
21天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
2月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
28 2
|
4月前
|
算法
测试工程师的技能升级:LeetCode算法挑战与职业成长
这篇文章通过作者亲身体验LeetCode算法题的过程,探讨了测试工程师学习算法的重要性,并强调了算法技能对于测试职业成长的必要性。
70 1
测试工程师的技能升级:LeetCode算法挑战与职业成长
|
4月前
|
算法
LeetCode第3题无重复字符的最长子串
该文章介绍了 LeetCode 第 3 题无重复字符的最长子串的解法,通过使用 HashSet 记录不重复的子元素,以每个字符开头遍历字符串,遇到重复字符则重新计算,最终找到最长子串,同时提到可以考虑使用 HashMap 降低复杂度。
LeetCode第3题无重复字符的最长子串
|
4月前
|
存储 算法 Java
LeetCode经典算法题:打家劫舍java详解
LeetCode经典算法题:打家劫舍java详解
70 2