3. 无重复字符的最长子串 - LeetCode

3. 无重复字符的最长子串

题目链接

最初的想法

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int ans = 0, i = 0, j = 0;
        HashSet<Character> set = new HashSet<>();
        while(i < s.length()){
            while(j < s.length() && !set.contains(s.charAt(j))){
                set.add(s.charAt(j));
                j++;
            }
            ans = Math.max(ans, j - i);
            if(j == s.length()){
                break;
            }
            while(set.contains(s.charAt(j))){
                set.remove(s.charAt(i));
                i++;
            }
        }
        return ans;
    }
}

很自然的,用两个指针表示子串头尾,用HashSet记录是否重复。但最终的速度不尽如人意,只超过35%的提交记录。

小优化——空间换时间

  1. 首先,i + ans >= s.length()就可以停止循环了,因为不可能出现更长的子串了。
  2. 其次,字符ASCLL码只有128种,可以用数组,虽然可能占用更大的空间,但查询、插入、删除等操作都只用O(1)的时间复杂度。

于是有了如下代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int ans = 0, i = 0, j = 0;
        boolean[] set = new boolean[128];
        while(i + ans < s.length()){
            while(j < s.length() && !set[s.charAt(j)]){
                set[s.charAt(j)] = true;
                j++;
            }
            ans = Math.max(ans, j - i);
            if(j == s.length()){
                break;
            }
            while(set[s.charAt(j)]){
                set[s.charAt(i)] = false;
                i++;
            }
        }
        return ans;
    }
}

这时用时超过了96%的提交记录,较为满意。

更多的空间换时间——记录字符上一次出现的位置

上面用的boolean数组记录了字符之前是否出现,但我们如果用int数组记录字符上一次出现的位置,就可以直接计算出子串长度,不需要两个指针,可以大大提升运行效率。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int[] last = new int[128];
        for(int i = 0; i < 128; i++){
            last[i] = -1;
        }
        int ans = 0, start = 0, n = s.length();
        for(int i = 0; i < n; i++){
            int index = s.charAt(i);
            start = Math.max(start, last[index] + 1);
            ans = Math.max(ans, i - start + 1);
            last[index] = i;
        }
        return ans;
    }
}

最终运行时间超过100%的提交记录。

可以看到改进后的代码较为简洁,先枚举子串结束位置,直接通过上一次出现的位置,计算出最长不重复子串的起始位置,省时省力。

posted @ 2021-01-16 13:27  一天到晚睡觉的鱼  阅读(97)  评论(0)    收藏  举报