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

 

思路1:滑动窗口

  设置一个滑动窗口与一个HashSet,用set来维护一个不重复的窗口。

  即当窗口右边界遇到窗口内重复的字符时,左边界移动至右边界处。

   res保存最大窗口大小,即为最长无重复字符的字串长度。

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0;
        Set<Character> set = new HashSet<>();
        char[] str = s.toCharArray();
        for (int i = 0, j = 0; i < s.length(); i++) {
            char c = str[i];
            while (set.contains(c)){
                set.remove(str[j]);
                j++;
            }
            set.add(c);
            res = Math.max(res, i - j + 1);
        }
        return res;
    }
}

 

优化:转用map来维护滑动窗口,移动窗口左边界时无需删除前面加入的字符,降低时间、空间消耗。

ps:

  由于滑动窗口是左开右闭区间,移动左边界直接到窗口中第一个重复字符所在位置,则窗口中仍保持字符不重复,且当前最大。

代码:

  时间复杂度O(n),空间复杂度O(1)

  (字符的 ASCII 码范围为 0 ~ 127 ,哈希表 dic 最多使用 O(128) = O(1) 大小的额外空间。)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0, start = -1;
        Map<Character, Integer> map = new HashMap<>();
        char[] str = s.toCharArray();
        for (int i = 0; i < s.length(); i++) {
            char c = str[i];
            if (map.containsKey(c)){
                start = Math.max(start, map.get(c));
            }
            map.put(c, i);
            res = Math.max(res, i - start);
        }
        return res;
    }
}

 

思路2:动态规划

  设动态规划列表dp,dp[j] 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。

  固定右边界 j ,设字符 s[j] 左边距离最近的相同字符为 s[i]。于是可分三种情况:

  1. 当 i < 0 ,即 s[j] 左边无相同字符,则 dp[j] = dp[j-1] + 1;
  2. 当 dp[j - 1] < j - i ,说明字符 s[i] 在子字符串 dp[j-1] 区间之外 ,则 dp[j] = dp[j - 1] + 1 ;
  3. 当 dp[j− 1] ≥ j − i ,说明字符 s[i] 在子字符串 dp[j-1] 区间之中 ,则 dp[j] 的左边界由 s[i] 决定,即 dp[j] = j − i ;

  同样通过map来统计各字符最后一次出现的索引位置,以及获取最近相同字符的索引。

ps:

Java 的 getOrDefault(key, default), 代表当哈希表包含键 key 时返回对应 value ,不包含时返回默认值 default 。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>();
        int res = 0, tmp = 0;
        for(int j = 0; j < s.length(); j++) {
            int i = dic.getOrDefault(s.charAt(j), -1); // 获取索引 i
            dic.put(s.charAt(j), j); // 更新哈希表
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
}

参考:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/solution/mian-shi-ti-48-zui-chang-bu-han-zhong-fu-zi-fu-d-9/
来源:力扣(LeetCode)
posted @ 2021-02-28 17:01  zjcfrancis  阅读(44)  评论(0编辑  收藏  举报