LeetCode 算法之旅 | 3. 无重复字符的最长子串

目录

    • 题目描述
    • 题解记录
      • 解法一:暴力破解
      • 自己写的解法
      • 滑动窗口+快慢指针
    • 参考资料

题目描述

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


示例 1:

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

示例 2:

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

示例 3:

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

—— 原文摘自:力扣(LeetCode | https://leetcode-cn.com/problems/longest-substring-without-repeating-characters)

题解记录

看到这道题,我第一时间排除了双重循环的暴力解法,因为时间复杂度最起码都在 O(n2)。但是呢,我自己写的也不咋地

解法一:暴力破解

class Solution {
  public int lengthOfLongestSubstring(String s) {
    int maxLength = 0;
    for (int i = 0; i < s.length(); i++) {
      List<Character> characters = new ArrayList<>();
      for (int j = i; j < s.length(); j++) {
        if (characters.contains(s.charAt(j))) {
          break;
        }
        characters.add(s.charAt(j));
        maxLength = Math.max(characters.size(), maxLength);
      }
    }
    return maxLength;
  }
}

由于我第一时间排除掉了暴力破解,所以我没有自己写暴力法,从众多大佬的题解中找了一个试了下,吓死宝宝了
LeetCode 算法之旅 | 3. 无重复字符的最长子串_第1张图片

自己写的解法

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int maxLen = 0;
        int tmpLen = 0;
        int maxRemoved = -1;
        Map<Character, Integer> map = new HashMap<>();
        for(int i = 0; i < s.length(); i++) {
            char current = s.charAt(i);
            if(map.containsKey(current)) {
                int repeatedIndex = map.get(current);
                if(repeatedIndex - maxRemoved == 1) {
                    for(int j = repeatedIndex; j > maxRemoved; j--) {
                        map.remove(current);
                    }
                    tmpLen = i - repeatedIndex;
                    map.put(current, i);
                    maxRemoved = repeatedIndex;
                } else {
                    maxLen = Math.max(tmpLen, maxLen);
					for(int j = repeatedIndex; j > maxRemoved;  j--) {
						map.remove(s.charAt(j));
					}
					map.put(current, i);
					maxRemoved = repeatedIndex;
					tmpLen = i - maxRemoved;
                }
            } else {
                map.put(current, i);
                tmpLen++;
            }
        }
        maxLen = tmpLen > maxLen ? tmpLen : maxLen;
        return maxLen;
    }
}

当时写的时候也没有想太多,只是想的最起码自己完整实现一个算法啊,于是就硬着头皮写了下来,最终也通过了所有测试案例。但是,性能并不是很好,而且可以看出来,相当复杂。
不过呢,事后再看了一遍我实现的这个算法,其实思想是对的,只是走了一些弯路,而且还有改进的空间,不过,也没继续往下改!

滑动窗口+快慢指针

关于滑动窗口解法,我自己并没有想出来,而是参考评论区中的各位大佬的,但是,由于每个人都有自己的思路,所以写出的代码并不尽相同,所以我仅仅只列出我参考的几个算法,并将我自己参考并自己最终写出的代码列出来。

  • 参考

    • 字符数组替代HashMap
    • 南星
    • 白金闪光蛋
    • 官方题解
    • 画解算法
    • 漫画图解
    • Java版4种解法,简单易懂,逐步优化
      以上参考链接中官方的题解个人感觉写得并不怎么好,画解算法算是理解起来最容易的,漫画图解重在突出了演进的过程,而4种解法则是展示了不同的题解思路。
      其中,我自己最初参考的是南星在评论中展示的解法,并作出了一些个人觉得好理解的改动(针对我自己而言).
  • 改动后我的题解

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int maxLen = 0;
        // 记录字符串中以每个字符为子串首字符时的子串开始位置;利用char和int相互转换的特性,相当于一个map,128为常用ASCII码字符表长度
        int[] last = new int[128];
        // 最长(结果)子串的开始位置,初始值为0
        int start = 0;
        for(int i = 0; i < s.length(); i++) {
            // 获取当前字符,将其转换为int,这样就不用使用map了
            int current = s.charAt(i);
            // 如果当前字符以前出现过,则更换子串,对新的子串进行运算
            start = Math.max(start, last[current]);
            // 计算出当前子串的长度,与上一次记录的最长子串的长度进行比较
            maxLen = Math.max(maxLen, i - start + 1);
            // 记录:如果以当前字符为子串首字符,那么对应子串的开始位置
            last[current] = i + 1;
        }
        return maxLen;
    }
}

虽然没有看完所有人的题解,但是从这道题中学到了一些东西的,感觉受益颇多!

参考资料

  • 字符数组替代HashMap
  • 南星
  • 白金闪光蛋
  • 官方题解
  • 画解算法
  • 漫画图解
  • Java版4种解法,简单易懂,逐步优化

你可能感兴趣的:(Java,算法,java,算法,leetcode)