LeetCode——3. 无重复字符的最长子串(Java)

LeetCode——3. 无重复字符的最长子串(Java)



题目

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

示例 1:

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

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

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


一、滑动窗口+哈希表

时间复杂度O(N);空间复杂度O(N)

  • 循环条件:right < s.length(),确保右边界不会超出字符串长度。当前字符:char current = s.charAt(right),获取当前右边界指向的字符。
  • 如果当前字符current已经在set中,说明当前窗口内出现了重复字符。此时,需要缩小窗口,通过将左边界left向右移动,并从set中移除left指向的字符。同时,窗口长度temp减1。
  • 如果当前字符current不在set中,说明当前窗口内没有重复字符。
    将当前字符current添加到set中。
    右边界right向右移动,窗口长度temp加1。
    更新最长无重复字符子串的长度ans,取ans和temp中的较大值。
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set=new HashSet<>();
        int left=0,temp=0,right=0,ans=0;
        while(right<s.length()){
            char current=s.charAt(right);
            if(set.contains(current)){
                set.remove(s.charAt(left));
                left++;
                temp--;
            }else{
                set.add(current);
                right++;
                temp++;
                ans=Math.max(ans,temp);
            }
        }
        return ans;
    }
}

初始状态:left = 0, right = 0, temp = 0, ans = 0,set = {}
第一步:right = 0,current = ‘a’,set = {‘a’},temp = 1,ans = 1
第二步:right = 1,current = ‘b’,set = {‘a’, ‘b’},temp = 2,ans = 2
第三步:right = 2,current = ‘c’,set = {‘a’, ‘b’, ‘c’},temp = 3,ans = 3
第四步:right = 3,current = ‘a’,set = {‘b’, ‘c’},left = 1,temp = 2
第五步:right = 4,current = ‘b’,set = {‘c’, ‘a’, ‘b’},temp = 3,ans = 3
以此类推,最终ans = 3。

二、滑动窗口+数组

时间复杂度O(N),空间复杂度O(N)

  • 当前字符的ASCII值:int index = s.charAt(i),获取当前字符的ASCII值。
  • 如果当前字符index之前出现过,last[index]记录的是该字符上次出现的位置加1。
    如果last[index]大于当前左边界left,说明当前字符在当前窗口内重复了,需要将左边界移动到last[index],从而跳过重复字符。
    如果last[index]小于或等于left,说明该字符上次出现的位置在当前窗口之外,不影响当前窗口,左边界保持不变。
  • 计算当前窗口的长度i - left + 1,并更新right为当前窗口长度和right中的较大值。
  • 将当前字符的最后出现位置更新为当前索引加1。
class Solution {
    public int lengthOfLongestSubstring(String s) {
       int[] last=new int[128];
       int left=0,right=0;
       for(int i=0;i<s.length();i++){
            int index=s.charAt(i);
            left=Math.max(left,last[index]);
            right=Math.max(right,i-left+1);
            last[index]=i+1;
       }
        return right;
    }
}

初始状态:left = 0, right = 0,last = {0, 0, …, 0}(128个0)
第一步:i = 0,index = ‘a’,last[‘a’] = 0,left = Math.max(0, 0) = 0,right = Math.max(0, 0 - 0 + 1) = 1,last[‘a’] = 1
第二步:i = 1,index = ‘b’,last[‘b’] = 0,left = Math.max(0, 0) = 0,right = Math.max(1, 1 - 0 + 1) = 2,last[‘b’] = 2
第三步:i = 2,index = ‘c’,last[‘c’] = 0,left = Math.max(0, 0) = 0,right = Math.max(2, 2 - 0 + 1) = 3,last[‘c’] = 3
第四步:i = 3,index = ‘a’,last[‘a’] = 1,left = Math.max(0, 1) = 1,right = Math.max(3, 3 - 1 + 1) = 3,last[‘a’] = 4
第五步:i = 4,index = ‘b’,last[‘b’] = 2,left = Math.max(1, 2) = 2,right = Math.max(3, 4 - 2 + 1) = 3,last[‘b’] = 5
以此类推,最终right = 3。

### Java 实现 LeetCode3 题:无重复字符长子 以下是基于滑动窗口算法Java 解决方案,该方法通过维护一个动态窗口来高效解决问题。此解法的时间复杂度为 O(n),空间复杂度为 O(min(m, n)),其中 m 是字符中不同字符的数量。 #### 滑动窗口原理 滑动窗口的核心思想是利用两个指针 `left` 和 `right` 来表示当前正在考察的子范围。当发现有重复字符时,移动左边界直到不再存在重复字符为止[^1]。 ```java public class Solution { public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) return 0; int maxLength = 0; int left = 0; // 左边界初始位置 Map<Character, Integer> charIndexMap = new HashMap<>(); for (int right = 0; right < s.length(); right++) { // 右边界逐步扩展 char currentChar = s.charAt(right); if (charIndexMap.containsKey(currentChar) && charIndexMap.get(currentChar) >= left) { // 如果当前字符已经存在于哈希表中,则更新左边界到上一次出现的位置之后 left = charIndexMap.get(currentChar) + 1; } charIndexMap.put(currentChar, right); // 更新或记录当前字符及其索引 maxLength = Math.max(maxLength, right - left + 1); // 计算大长度 } return maxLength; } } ``` 上述代码实现了以下逻辑: - 使用 `HashMap` 存储每个字符近一次出现的索引。 - 当遇到重复字符时,调整左边界至之前重复字符的下一个位置。 - 动态计算并保存大的无重复子长度[^2]。 #### 测试案例 为了验证程序的有效性,可以运行如下测试: ```java public static void main(String[] args) { Solution sol = new Solution(); System.out.println(sol.lengthOfLongestSubstring("abcabcbb")); // 输出: 3 ["abc"] System.out.println(sol.lengthOfLongestSubstring("bbbbb")); // 输出: 1 ["b"] System.out.println(sol.lengthOfLongestSubstring("pwwkew")); // 输出: 3 ["wke"] System.out.println(sol.lengthOfLongestSubstring("")); // 输出: 0 [] System.out.println(sol.lengthOfLongestSubstring("au")); // 输出: 2 ["au"] } ``` 这些测试涵盖了多种情况,包括空字符、全相同字符以及正常输入场景下的表现[^3]。 #### 复杂度分析 时间复杂度:O(n),因为每个字符多被访问两次——一次由右指针遍历,另一次可能因左指针移动而重新评估。 空间复杂度:O(min(m, n)),取决于字符中的唯一字符数量与总长度之间的较小者[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值