无重复字符的最长子串

136 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

前言

笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。

系列文章收录《算法》专栏中。

力扣题目链接

问题描述

给定一个字符串 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 由英文字母、数字、符号和空格组成

剖析

暴力破解

笔者第一反应能想到的就是暴力破解,利用两层循环进行比对是否重复,如果重复就从下一个字符开始往后继续比对,期间可以使用HashSet进行查看是否存在相同的字符,当从下一个开始时记得把上一个字符给移除掉。

这里代码就不写了,我们来看下它的复杂度:

  • 时间复杂度:O(n^2),两层循环。
  • 空间复杂度:HashSet最长时应该就是最长字串的长度,设长度为M,那么就是O(M)。

滑动窗口

由于笔者刚写完《滑动窗口最大值》这道题,所以笔者很容易想到滑动窗口,但是这个很明显是动态的,当遇到重复的就缩小(继续往下一个字符走,滑过的移除),遇到不重复的扩张,这滑动的时候我们需要记录当前不重复的子字符串长度,每次都取较大的最后滑完就是“最长的不重复子字符串”的长度。

下面我们描述下具体步骤:

  1. 从第一个字符开始,把第一个字符装进HashSet中。
  2. 扩大滑块把下一个字符和HashSet中的已存在的字符进行对比。
  3. 如果在HashSet中不存在就把这个字符放入HashSet中,更新较大“不重复子字符串”长度,继续重复第二三步骤。如果发现存在就滑动滑块,把滑过去的字符从HashSet中移除,然后重复第二三步骤。

复杂度:

  • 时间复杂度:很明显滑块要从头滑到尾,所以复杂度为O(n)。
  • 空间复杂度:应该和暴力破解中的一样为O(M)。

下面我们直接看代码。

代码

public static int lenghtOfLongestSubstring(String s) {
    int result = 0;

    Set<Character> characterSet = new HashSet<>();

    int rt = 0;
    for (int i = 0; i < s.length(); i++) {
        if (i > 0) {
            characterSet.remove(s.charAt(i - 1));
        }

        while (rt < s.length() && !characterSet.contains(s.charAt(rt))) {
            characterSet.add(s.charAt(rt));
            rt++;
        }

        result = Math.max(result, characterSet.size());
    }

    return result;
}