Java详解LeetCode 热题 100(08):LeetCode 3. 无重复字符的最长子串详解

1. 题目描述

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

示例 1:

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

示例 2:

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

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是子串的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 10^4
  • s 由英文字母、数字、符号和空格组成

2. 理解题目

这道题要求我们找出给定字符串中不包含重复字符的最长子串的长度。我们需要理解以下几点:

  1. 子串 vs 子序列:子串是原字符串中连续的字符序列,而子序列可以是原字符串中不连续的字符序列。本题求的是子串。
  2. 无重复字符:子串中不能有重复出现的字符。
  3. 最长:如果有多个无重复字符的子串,我们只关心最长的那个。

关键点:

  • 我们需要判断子串中是否有重复字符
  • 我们需要记录遇到的每个字符
  • 我们需要计算无重复字符子串的长度,并找出最大值

3. 解法一:暴力法

3.1 思路

最直观的方法是检查所有可能的子串,判断它们是否包含重复字符,然后找出最长的。

具体算法:

  1. 遍历字符串的所有可能的子串
  2. 对于每个子串,检查是否包含重复字符
  3. 如果不包含重复字符,更新最大长度

3.2 Java代码实现

public class Solution {
   
    public int lengthOfLongestSubstring(String s) {
   
        int n = s.length();
        int maxLength = 0;
        
        // 遍历所有可能的子串起点
        for (int i = 0; i < n; i++) {
   
            // 遍历所有可能的子串终点
            for (int j = i; j < n; j++) {
   
                // 检查子串s[i...j]是否包含重复字符
                if (allUnique(s, i, j)) {
   
                    maxLength = Math.max(maxLength, j - i + 1);
                }
            }
        }
        
        return maxLength;
    }
    
    // 检查子串s[start...end]是否包含重复字符
    private boolean allUnique(String s, int start, int end) {
   
        Set<Character> set = new HashSet<>();
        
        for (int i = start; i <= end; i++) {
   
            char ch = s.charAt(i);
            // 如果字符已存在于集合中,则包含重复字符
            if (set.contains(ch)) {
   
                return false;
            }
            set.add(ch);
        }
        
        return true;
    }
}

3.3 代码详解

  1. 我们使用两个嵌套循环来遍历所有可能的子串:
    • 外循环变量i表示子串的起始位置
    • 内循环变量j表示子串的结束位置
  2. 对于每个子串s[i...j],我们调用allUnique方法来检查它是否包含重复字符
  3. allUnique方法使用Set来跟踪已经出现过的字符:
    • 如果遍历过程中发现字符已存在于集合中,则返回false
    • 否则,将字符添加到集合中,并继续检查下一个字符
    • 如果遍历完成没有发现重复字符,则返回true
  4. 如果子串不包含重复字符,我们更新最大长度maxLength

3.4 复杂度分析

  • 时间复杂度:O(n³),其中n是字符串的长度
    • 外循环执行n次
    • 内循环最多执行n次
    • allUnique方法需要O(j-i)的时间,最坏情况下是O(n)
    • 总时间复杂度为O(n²) * O(n) = O(n³)
  • 空间复杂度:O(k),其中k是字符集的大小。在最坏的情况下,allUnique方法中的Set将存储所有可能的字符。

3.5 问题与改进

暴力法简单直观,但效率很低。对于每个子串,我们都要重新检查所有字符是否有重复,这导致了许多重复计算。我们可以通过滑动窗口方法来优化。

4. 解法二:滑动窗口法

4.1 思路

滑动窗口是一个很常用的双指针技巧,我们可以用它来解决这个问题。窗口的左右边界表示当前正在考虑的子串的范围。

具体算法:

  1. 使用两个指针(左指针和右指针)表示一个窗口
  2. 右指针不断向右移动,扩大窗口,并将字符加入到集合中
  3. 当遇到重复字符时,左指针向右移动,缩小窗口,直到重复的字符被移出窗口
  4. 在整个过程中,维护窗口的最大长度

4.2 Java代码实现

public class Solution {
   
    public int lengthOfLongestSubstring(String s) {
   
        int n = s.length();
        Set<Character> set = new HashSet<>();
        int maxLength = 0;
        int left 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈凯哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值