【剑指offer刷题日记】48. 最长不含重复字符的子字符串

147 阅读2分钟

题目

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例1:

输入: "abcabcbb" 输出: 3

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

解法一 动态规划加Map

运用动态规划即我们可以不停的看我们当前的这个字母有没有在原来出现过,如果没有出现过,则最长的长度为前一位的最长加1,如果出现过,我们需要看这个出现的字母存不存在我们现在的子字符串中,存在则我们当前所看的这个子序列最长为这两个重复的字母中间的数字,不存在则还是前一位的最长加一。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() < 2) return s.length();
        Map<Character,Integer> dic = new HashMap<>();

        int cur = 0; int result = 0;
        for(int end = 0; end < s.length();end++){
            // 如果出现过,则start为上一次出现的数组下标,没有则为1,说明我们可以从头计算
            int start = dic.getOrDefault(s.charAt(end),-1);
            dic.put(s.charAt(end),end);
            
            // 如果现在这个字母下标减去最长子序列长度,还是大于上一次出现的字母下标,
            // 说明上一次的重复并不在我们现在计算的这个子字符串里,现在的子字符串可以
            // 加上现在的这个字母,即长度+1
            if(end-cur > start){
                cur = cur+1;
            // 如果上一次的重复出现在我们现在的子字符串里,那我们最长无重复的子字符串
            // 长度只能为这两个重复字母之间的距离
            }else{
                cur = end-start;
            }
            result = Math.max(cur,result);
        }
        return result;
    }
}

时间复杂度:O(n)

空间复杂度:O(1)

解法二 双指针加Map

我们可以维护两个指针,一个是我们子字符串开始的位置,start,另一个是我们子字符串结束的位置,end。我们一直向后挪动我们的end,然后每次找前面有没有和他重复的,如果没有,则现在最长的子字符串为开始到end中间这些字符,如果出现了,那我们现在暂时最长的的子字符串长度为end-start,我们再看这个暂时的值是不是大于我们前面遇到的无重复子字符串的长度

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() < 2) return s.length();
        Map<Character,Integer> dic = new HashMap<>();

        int start = -1; int result = 0;
        for(int end = 0;end < s.length();end++){
            if(dic.containsKey(s.charAt(end))){
                // 我们需要取子字符串的原开头和重复字母的位置中更大的那个
                start = Math.max(start,dic.get(s.charAt(end)));
            }
            dic.put(s.charAt(end),end);
            result = Math.max(result,end-start);
        }
        return result;
    }
}

时间复杂度:O(n)

空间复杂度:O(1)