力扣-3-无重复字符的最长子串

一、题目理解

题目描述

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

示例
  • 示例 1:输入 s = "abcabcbb",输出 3(最长子串是 "abc",长度为 3);
  • 示例 2:输入 s = "bbbbb",输出 1(最长子串是 "b",长度为 1);
  • 示例 3:输入 s = "pwwkew",输出 3(最长子串是 "wke" 或 "kew",长度为 3)。
关键说明
  • 子串:是字符串中连续的字符序列(区别于子序列,子序列可以不连续);
  • 无重复:子串中每个字符都只能出现一次。

二、解题思路

这道题的最优解法是滑动窗口(双指针)+ 哈希表,核心逻辑是:

  1. 用两个指针(左指针 left、右指针 right)表示当前的"无重复子串窗口";
  2. 右指针向右移动,逐个加入字符,用哈希表(或数组)记录字符的最新位置;
  3. 若右指针遇到重复字符,且该字符在当前窗口内(位置 ≥ left),则将左指针移动到"重复字符的下一位",保证窗口内始终无重复;
  4. 每次移动右指针,计算当前窗口长度,更新最大长度。
思路对比(新手友好)
  • 暴力解法:枚举所有子串,判断是否有重复,时间复杂度 O(n2)O(n^2)O(n2),效率低;
  • 滑动窗口:仅遍历字符串一次,时间复杂度 O(n)O(n)O(n),是最优解。

三、Java代码实现(最优解)

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 记录字符的最新索引(ASCII字符范围0-127,用数组比HashMap更高效)
        int[] charIndex = new int[128];
        // 初始化数组值为-1(表示字符未出现过)
        for (int i = 0; i < 128; i++) {
            charIndex[i] = -1;
        }
        
        int maxLen = 0; // 记录最长无重复子串长度
        int left = 0;   // 滑动窗口左指针(窗口起始位置)
        
        // 右指针right遍历字符串,逐个加入字符
        for (int right = 0; right < s.length(); right++) {
            char currChar = s.charAt(right);
            // 若当前字符已出现,且在当前窗口内(索引≥left),则移动左指针到重复字符的下一位
            if (charIndex[currChar] != -1 && charIndex[currChar] >= left) {
                left = charIndex[currChar] + 1;
            }
            // 更新当前字符的最新索引
            charIndex[currChar] = right;
            // 计算当前窗口长度,更新最大值
            maxLen = Math.max(maxLen, right - left + 1);
        }
        
        return maxLen;
    }
}

四、代码关键解析

  1. 数据结构选择(数组 vs HashMap)

    • 为什么用 int[128] 而不是 HashMap<Character, Integer>?
      字符串的字符本质是ASCII码(范围0-127),数组访问速度比哈希表更快,且无需处理哈希冲突,空间复杂度仅 O(1)O(1)O(1)(数组大小固定为128)。
    • 数组值的含义:charIndex[c] 表示字符 c 最后一次出现的索引,初始值 -1 表示未出现。
  2. 滑动窗口核心逻辑

    以 s = "abcabcbb" 为例,分步拆解:

    • right=0(字符'a'):charIndex['a']=-1,窗口 0,0,长度1,maxLen=1,更新 charIndex['a']=0;
    • right=1(字符'b'):charIndex['b']=-1,窗口 0,1,长度2,maxLen=2,更新 charIndex['b']=1;
    • right=2(字符'c'):charIndex['c']=-1,窗口 0,2,长度3,maxLen=3,更新 charIndex['c']=2;
    • right=3(字符'a'):charIndex['a']=0 ≥ left=0,左指针移到 0+1=1,窗口 1,3,长度3,maxLen仍为3,更新 charIndex['a']=3;
    • 后续遍历:窗口会动态调整,但maxLen始终保持3,最终返回3。
  3. 关键判断条件
    charIndex[currChar] != -1 && charIndex[currChar] >= left:

    • 第一个条件:字符是否出现过;
    • 第二个条件:字符的上一次出现位置是否在当前窗口内(比如字符'a'之前出现在索引0,但左指针已移到1,说明该重复字符不在窗口内,无需调整左指针)。
  4. 窗口长度计算
    right - left + 1:比如窗口是 1,3,长度是 3-1+1=3(对应字符'b','c','a')。

五、边界情况测试

  • 空字符串:s="",返回0;
  • 单字符:s="a",返回1;
  • 全重复:s="bbbbb",返回1;
  • 无重复:s="abcd",返回4;
  • 重复字符在窗口外:s="abba",right=3(字符'a')时,charIndex['a']=0 < left=2,无需调整左指针,窗口 2,3,长度2。

总结

  1. 核心解法:滑动窗口(双指针) 是解决"最长无重复子串"类问题的最优思路,时间复杂度 O(n)O(n)O(n)(仅遍历一次字符串);
  2. 优化技巧:用固定大小的数组 替代HashMap存储字符索引,兼顾时间和空间效率(空间复杂度 O(1)O(1)O(1));
  3. 关键细节:调整左指针时,必须保证左指针只向右移动(避免回退),且仅当重复字符在当前窗口内时才调整。

这道题是滑动窗口的入门经典题,掌握后可以举一反三解决类似的"子串/子数组"问题(比如"最小覆盖子串"),核心是理解"窗口的动态调整逻辑"。

相关推荐
镜子AI几秒前
教培机构多模态内容跨模态检索系统:基于CLIP的统一表征与图文混合召回算法
人工智能·python·算法·机器学习
小宋102111 分钟前
合成评测集怎么造才不“自嗨”:自动出题、去重、泄漏检测与人工抽检
人工智能·算法·机器学习
蒸蒸yyyyzwd24 分钟前
秋招笔记day58
算法·哈希算法
Jasmine_llq34 分钟前
《P5663 [CSP-J 2019] 加工零件》
数据结构·算法·bfs·广度优先搜索·分层 bfs /·奇偶分层最短路·奇数最短步数
在所不辞兄42 分钟前
分层PINN提升多物理场一致性
人工智能·神经网络·算法·机器学习·工程仿真
wanderist.43 分钟前
CSP 第43次认证 T4:集合划分 —— 图染色与二分图判定
算法·图论
不做无法实现的梦~1 小时前
基于 PX4 的全仓库架构、无人机算法解析与开发学习规划
算法·架构·无人机
做cv的小昊1 小时前
【大模型算法自学笔记01】NLP基础知识(1.1 自注意力)
人工智能·笔记·算法·自然语言处理·大模型·llm
stolentime1 小时前
AT_arc182_a [ARC182A] Chmax Rush!题解
数据结构·c++·算法·动态规划
longlongzihan1 小时前
LeetCode 189. 轮转数组:从直观模拟到最优原地算法
c++·算法·leetcode