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. python3的json数据库-TinyDB初入门

    无意间看到TinyDB这个词汇,就去查了一下,就发现了它的官方网站 这里 然后就是按照他说的步骤去做. 第1步 安装  pip3 install tinydb 安装成功后,创建一个文件名字叫做 tes ...

  2. MySQL 主从复制之多线程复制

    目录 一.MySQL 多线程复制的背景 二.MySQL 5.5 主从复制 1.原理 2.部署主从复制 2.1.主节点安装配置MySQL 5.5 2.2.从节点安装配置MySQL 5.5 3.检查主从库 ...

  3. ECharts 引入中国地图和区域地图

    一,引入中国地图   <div  id="chinaMap"></div> import china from 'echarts/map/js/china. ...

  4. element ui table+分页 筛选全部数据

    1. @filter-change 要写在table根元素,也就是<el-table @filter-change="filterChange"></el-tab ...

  5. Windows 使用 Intel(R) Arc(TM) GPU 推理ONNX 模型

    这不刚换了一个笔记本电脑,Thinkpad T14P,带有Intel ARC GPU,今天我们来尝试用这个GPU来推理ONNX模型. 环境安装 查阅了相关文档,最好使用py310环境,其他版本可能存在 ...

  6. Winform在主窗体加载前弹出登录窗体

    1:主窗体代码 点击查看代码 //实例化登录窗体 FrmLogin frmLogin = new FrmLogin(); //读取登录窗体的返回结果 DialogResult dialogResult ...

  7. github访问不了解决方法

    github突然无法访问,解决办法如下-迷恋自留地 首先通过网址https://tool.chinaz.com/dns?type=a&host=github.com 修改hosts文件,win ...

  8. C++ builder 10.2 x64程序使用typeid获取vcl类名时异常

    C++ builder 10.2 x64程序使用typeid获取vcl类名时异常 比如: const std::type_info &t= typeid(TForm1); 那么t的name() ...

  9. Nginx转发解析长域名多路径域名为一级域名

    ​Nginx解析短域名,例如:访问 http://192.168.1.23 可直接跳转到 http://192.168.1.23/webroot/decision server { listen 90 ...

  10. Linux 添加开机自启动

    rc.local 方式 一.& 在 Linux 命令后加上 &  可以在后台运行 二.nohup 对 SIGHUP 信号免疫,对 SIGINT 信号不免疫,可用 shopt | gre ...