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

点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)

生活中的算法

你是否玩过"一指禅"游戏?就是沿着一串字母走,不能重复走过已经走过的字母。这个游戏的本质,其实就是在寻找无重复字符的最长子串。

在实际编程中,这个问题的应用非常广泛。比如在文本编辑器中查找不含重复字符的最长片段,或是在DNA序列分析中寻找无重复碱基对的最长序列。

问题描述

LeetCode第3题"无重复字符的最长子串"是这样描述的:给定一个字符串 s,请你找出其中不含有重复字符的最长子串的长度。

例如:

  • 输入: s = “abcabcbb”,输出: 3(最长子串是 “abc”)
  • 输入: s = “bbbbb”,输出: 1(最长子串是 “b”)
  • 输入: s = “pwwkew”,输出: 3(最长子串是 “wke”)

最直观的解法:暴力枚举法

最容易想到的方法是:枚举所有可能的子串,检查每个子串是否包含重复字符,然后找出最长的那个。

让我们用一个例子来模拟这个过程:

s = "pwwk"

检查所有子串:
"p" - 长度1,无重复
"pw" - 长度2,无重复
"pww" - 长度3,有重复
"pwwk" - 长度4,有重复
"w" - 长度1,无重复
"ww" - 长度2,有重复
"wwk" - 长度3,有重复
"w" - 长度1,无重复
"wk" - 长度2,无重复
"k" - 长度1,无重复 最长的无重复子串长度为2

这种思路可以用Java代码这样实现:

public int lengthOfLongestSubstring(String s) {
int maxLength = 0; // 枚举所有可能的起点
for (int i = 0; i < s.length(); i++) {
// 使用Set来检查是否有重复字符
Set<Character> charSet = new HashSet<>();
int currentLength = 0; // 从起点开始尝试延伸
for (int j = i; j < s.length(); j++) {
// 如果遇到重复字符,结束当前子串的检查
if (charSet.contains(s.charAt(j))) {
break;
}
charSet.add(s.charAt(j));
currentLength++;
} maxLength = Math.max(maxLength, currentLength);
} return maxLength;
}

优化解法:滑动窗口法

仔细观察会发现,当我们遇到重复字符时,不需要完全重新开始,而是可以从上一次该字符出现位置的下一个位置继续。这就是"滑动窗口"的思想。
举个例子,对字符串"abcdce",当我们查看到"abcd"这个子串时,子串内没有重复字符。
但是,当我们继续前进,子串变成"abcdc",现在c重复了!由于出现了第二个c,所以,第一个c之前的字符,都没有用了。
我们需要把第一个c,以及它前面的字符全部剔除出去,以保证c不再重复。

滑动窗口法的原理

  1. 使用两个指针(left和right)维护一个窗口
  2. 右指针不断向右移动,扩大窗口
  3. 当遇到重复字符时,左指针移动到上一次该字符出现位置的下一位
  4. 在这个过程中记录最大窗口大小

算法步骤(伪代码)

  1. 初始化left = 0,right = 0,maxLength = 0
  2. 使用Map记录每个字符最后出现的位置
  3. 移动右指针,对于每个字符:
    • 如果字符已在窗口中,更新left指针
    • 更新字符的位置
    • 更新最大长度

示例运行

让我们用s = "abba"模拟这个过程:

初始状态:left = 0, right = 0, maxLength = 0
Map = {} 1. 处理'a':
Map = {a:0}
窗口:[a]
maxLength = 1 2. 处理'b':
Map = {a:0, b:1}
窗口:[ab]
maxLength = 2 3. 处理'b':
发现重复的'b'
left移动到上一个'b'的下一位
Map = {a:0, b:2}
窗口:[b]
maxLength = 2 4. 处理'a':
发现重复的'a'
left移动到上一个'a'的下一位
Map = {a:3, b:2}
窗口:[ba]
maxLength = 2

Java代码实现

public int lengthOfLongestSubstring(String s) {
// 使用HashMap存储字符最后出现的位置
Map<Character, Integer> charMap = new HashMap<>();
int maxLength = 0; // left是窗口左边界,i是右边界
for (int left = 0, i = 0; i < s.length(); i++) {
char currentChar = s.charAt(i); // 如果字符已经在窗口中,更新左边界
if (charMap.containsKey(currentChar)) {
// 取max是为了防止left向左移动
left = Math.max(left, charMap.get(currentChar) + 1);
} // 更新字符位置和最大长度
charMap.put(currentChar, i);
maxLength = Math.max(maxLength, i - left + 1);
} return maxLength;
}

解法比较

让我们比较这两种解法:

暴力枚举法:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(min(m,n)),其中m是字符集大小
  • 优点:直观易懂
  • 缺点:效率低,有重复计算

滑动窗口法:

  • 时间复杂度:O(n)
  • 空间复杂度:O(min(m,n))
  • 优点:一次遍历就能得到结果
  • 缺点:需要额外空间存储字符位置

题目模式总结

这道题体现了几个重要的算法思想:

  1. 滑动窗口:使用双指针维护一个符合条件的区间
  2. 空间换时间:使用哈希表记录信息来优化查找
  3. 重复利用信息:不重新开始,而是利用已知信息继续搜索

这种解题模式在很多问题中都有应用,比如:

  • 最小覆盖子串
  • 字符串的排列
  • 找到字符串中所有字母异位词

解决此类问题的通用思路是:

  1. 考虑是否可以通过维护一个窗口来解决
  2. 确定窗口的更新条件
  3. 想清楚如何移动左右指针
  4. 考虑是否需要额外的数据结构来优化

小结

通过这道题,我们不仅学会了如何找到最长无重复子串,更重要的是掌握了滑动窗口这个强大的算法技巧。从暴力解法到优化解法,我们看到了如何通过观察问题特点来优化算法。

记住,很多看似复杂的问题,都可以通过滑动窗口来优雅地解决。当你遇到类似的字符串处理问题时,不妨先想想是否可以用这个技巧!


作者:忍者算法
公众号:忍者算法

从一指禅到无重复字符:最长子串问题的优雅解法|LeetCode 3 无重复字符的最长子串的更多相关文章

  1. [LeetCode] Longest Substring Without Repeating Characters 最长无重复字符的子串

    Given a string, find the length of the longest substring without repeating characters. Example 1: In ...

  2. [LeetCode] Longest Substring Without Repeating Characters 最长无重复字符的子串 C++实现java实现

    最长无重复字符的子串 Given a string, find the length of the longest substring without repeating characters. Ex ...

  3. 【LeetCode】无重复字符的最长子串【滑动窗口法】

    给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc&quo ...

  4. LeetCode 3. 无重复字符的最长子串(Longest Substring Without Repeating Characters)

    题目描述 给定一个字符串,找出不含有重复字符的最长子串的长度. 示例: 给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3. ...

  5. 滑动窗口经典题 leetcode 3. 无重复字符的最长子串

    题目 解题思路 题目要求找出给定字符串中不含有重复字符的最长子串的长度.这是一个典型的滑动窗口的题目,可以通过滑动窗口去解答. 滑动窗口 具体操作如下图示:找到一个子串 s[left...right] ...

  6. 【Leetcode】无重复字符的最长子串

    暴力解法,枚举所有子字符串组合 输入:长度[0,n]的字符串 耗时过长--- class Solution { public: int lengthOfLongestSubstring(string ...

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

    1. 题目 2. 解答 2.1. 方法一 我们从前往后遍历字符串,start 代表最长子串的起始位置,一开始设置为零. 如果没有遇到重复字符,则更新子串的长度,向后遍历. 如果遇到重复字符时,则更新字 ...

  8. LeetCode OJ -- 无重复字符的最长子串

    给定一个字符串,找出不含有重复字符的 最长子串 的长度. 示例: 给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3. 给定  ...

  9. [LeetCode] Longest Substring Without Repeating Characters 最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  10. 【LeetCode】无重复字符串最长子串

    题目描述 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "a ...

随机推荐

  1. 牛客小白月赛105 (Python题解)

    牛客小白月赛105 (Python题解) 比赛链接:点击传送 A-lz的吃饭问题 代码: a, b = map(int,input().split()) c, d = map(int,input(). ...

  2. jetty嵌入式配置总结

    Jetty配置到项目 拷贝lib/jsp和lib下的所有jar包到项目中. 在工程中创建文件夹jetty/etc.jetty/contexts 拷贝jetty包中的jetty.xml和webdefau ...

  3. 关于XML文档和JAVA中的JTree之间如何转换的问题

    关于XML文档和JAVA中的JTree之间如何转换的问题 XML因为良好的结构,被广泛地应用于文档格式的定义.我们知道,应用软件一般需要用配置文件来决定运行时的一些 参数.以前的应用程序的配置文件一般 ...

  4. Docker之磁盘清理

    Docker 很占用空间,每当我们运行容器.拉取镜像.部署应用.构建自己的镜像时,我们的磁盘空间会被大量占用. 如果你也被这个问题所困扰,咱们就一起看一下 Docker 是如何使用磁盘空间的,以及如何 ...

  5. require.js 笔记

    1.前言 随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作.模块复用.单元测试等等一系列 ...

  6. java 去重元素,元素是一组没有顺序的字符

    1.需求描述: 有一个大集合,大集合中的元素是是一个小集合,要求在大集合中的小集合不能重复,小集合中的元素没有顺序. 例如有个大集合 [[a,b],[b,c]] 向这个元素中添加元素[b,a]就是添加 ...

  7. 再用RNN神经网络架构设计生成式语言模型

    上一篇:<用谷歌经典ML方法方法来设计生成式人工智能语言模型> 序言:市场上所谓的开源大语言模型并不完全开源,通常只提供权重和少量工具,而架构.训练数据集.训练方法及代码等关键内容并未公开 ...

  8. Centos7.8安装Gitlab

    公司为了合规性考虑,需要自己搭建私有化版的github.那不用想,肯定要上GitLab了. 项目背景: 服务器:华为云ECS,需要上公网,并在安全组打开80端口访问. 用户:关闭公开注册,新建用户后, ...

  9. node-koa2 微信支付-企业付款到银行卡

    微信支付用的V2版本 微信支付说明文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay_yhk.php?chapter=24_2   参数详细 ...

  10. JS 面试题(2023-09-20更新)

    因JS代码实现面试题较多,移至另外一篇文章:JS面试题-代码实现 基础 JavaScript 是什么? JavaScript 是一种属于网络的脚本语言,被广泛应用于 Web 应用开发 JavaScri ...