Loading

力扣 - 剑指 Offer 48. 最长不含重复字符的子字符串

题目

剑指 Offer 48. 最长不含重复字符的子字符串

思路1(动态规划+哈希表)

  • 暴力查找的所花时间复杂度为\(O(N^3)\),太高了,不推荐
  • 这题我们可以使用动态规划,我们定义dp[j]为:以第j个字符为结尾的不包含重复字符的子字符串的最大长度。我们需要从左到右依次扫描字符串中的每个字符,同时使用哈希表存储每个字符出现的最后一次位置,如果第一次出现,就记为-1,因此总共可能出现的情况如下:
    1. 如果第j个字符之前没有出现过(j < 0),那么dp[j] = dp[j-1] + 1,即没出现过字符,那么我们长度可以直接在前一个基础上直接加上1
    2. 我们记第j个字符上一次出现的位置和当前位置的距离为d(j-i),如果第j个字符在此之前出现过,那么又分两种情况:
      1. d > dp[j-1],即上一个重复的字符在dp[j-1]区间外,这对当前没有影响,因此dp[j] = dp[j-1] + 1
      2. d < dp[j-1],上一个重复的字符在dp[j-1]区间内,这时候,因为要不重复嘛,所以dp[j] = j - i
  • 因此,根据上面的推到,我们可以得到状态转移方程\(dp(j)= \begin{cases} dp[j-1]+1& \text{dp[j - 1] < j - i}\\ j-i& \text{dp[j - 1] <= j - i} \end{cases}\)
  • 因为我们是需要最大值,所以可以将dp数组优化成使用temp存储,使用restemp中记录最大值

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Character, Integer> map = new HashMap<>();
        char[] cs = s.toCharArray();
        int length = s.length();
        int temp = 0;
        int res = 0;

        for (int j = 0; j < length; j++) {
            // 获取当前字符上一次的出现位置,第一次的话返回-1
            int i = map.getOrDefault(cs[j], -1);
            // 更新字符出现的位置
            map.put(cs[j], j);
            // 得到以当前字符结尾的最大长度
            temp = temp < j - i ? temp + 1 : j - i;
            // res选择最大值
            res = Math.max(res, temp);
        }

        return res;
    }
}

复杂度分析

  • 时间复杂度:\(O(N)\)
  • 空间复杂度:\(O(1)\)

思路2(滑动窗口)

  • 定义个一个start用于控制滑动窗口的左边界(start只能往右不能往左)
  • 从左到右遍历数组,同时用哈希表记录每个字符最后出现的位置,然后每次将窗口的大小与res比较获取较大的
  • 因此start ~ i区间内无重复字符的窗口大小为i - startstart = Math.max(start, map.get(cs[i]));这个保证了有重复的就会更新start跳过)

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] cs = s.toCharArray();
        int length = s.length();

        HashMap<Character, Integer> map = new HashMap<>();
        int res = 0;
        // 滑动窗口的左边界
        int start = -1;

        for (int i = 0; i < length; i++) {
            // 窗口左边界start的位置
            if (map.containsKey(cs[i])) {
                start = Math.max(start, map.get(cs[i]));
            }
            // 更新当前字符最后出现的位置
            map.put(cs[i], i);
            // res获取窗口最大值即为最大长度
            res = Math.max(res, i - start);
        }

        return res;
    }
}

复杂度分析

  • 时间复杂度:\(O(N)\)
  • 空间复杂度:\(O(1)\)
posted @ 2021-11-12 10:13  linzeliang  阅读(43)  评论(0)    收藏  举报