LeetCode3

版权声明:有些文章写的很随意,那是仅作为个人记录的文章,建议直接关掉,多看一秒亏一秒 https://blog.csdn.net/qq_36523667/article/details/85225954

题目 

最长不重复子串

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

思路

做了LeetCode1,那题是找到唯一的a+b=target,其遍历到a,计算出b,再去发现是否contains,和这题很类似。所以直接Hash的思路。

暴力思路

以a[0],a[1]...开头,其结果各不一样,所以n*n

滑动窗口优化思路

我一直以来都知道复用的思想,看了官方答案,知道了这叫滑动窗口。

当a[0]确定,然后向后遍历,到某一点终止,得出a[0]下的最优解时,其实对于a[1],这个解也是成立的,同时由于少了第一个数,可以继续向后遍历试试

动态规划思路

假设前n个数已得出最优解,且最优解已包含最后一个数,那么新的后面的一个数,如果和之前有重复,那么需要从重复的那一位开始到当前,这是新的解集,1;反之,这个位上的最优解要多1个,+1。

暴力代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        Set<Character> set = new HashSet<>();
        int max = 0;
        for(int i = 0; i < len; i ++) {
            int total = 0;
            for(int j = i; j < len; j ++) {
                Character c = (Character)chars[j];
                if(!set.contains(c)) {
                    total ++;
                    set.add(c);
                } else {
                    break;
                }
            }
            max = Math.max(max, total);
            set.clear();
        }
        return max;
    }
}

滑动窗口优化代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        Set<Character> set = new HashSet<>();
        int max = 0;
        int endIndex = 0;
        for(int i = 0; i < len; i ++) {
            Character c = (Character)chars[i];
            if(!set.contains(c)) {
                max ++;
                set.add(c);
            } else {
                endIndex = i;//从这里开始还没扫过
                break;
            }
        }
        if(endIndex == 0) {//如果第一遍能顺利遍历,那么就是整长啊
            return max;
        }
        int lastTotal = max;
        for(int i = 1; i < len; i ++) {
            Character last = (Character)chars[i - 1];
            set.remove(last);//滑动窗口第一个数不要了
            lastTotal --;
            for(int j = endIndex; j < len; j ++) {//从滑动窗口的末端开始找
                Character c = (Character)chars[j];
                if(!set.contains(c)) {
                    lastTotal ++;
                    set.add(c);
                    endIndex = j + 1;
                } else {
                    endIndex = j;
                    break;
                }
            }
            max = Math.max(max, lastTotal);
        }
        return max;
    }
}

动态规划代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        Map<Character, Integer> map = new HashMap<>();
        int max = 0;
        int total = 0;
        int startIndex = 0;//上个解的startIndex
        for(int i = 0; i < len; i ++) {
            Character c = (Character)chars[i];
            if(!map.containsKey(c)) {
                total ++;
                max = Math.max(max, total);
            } else {
                int index = map.get(c);
                //包含这个数,前面的所有数都要被扔掉
                for(int j = startIndex; j <= index; j ++) {
                    map.remove(chars[j]);
                }
                startIndex = index + 1;
                //total等于[startIndex,i]
                total = i - startIndex + 1;
            }
            map.put(c, i);
        }
        return max;
    }
}

结果我发现我的各种方法都和官方有出入

我的暴力对比官方暴力

官方是枚举2个点,然后开始进行运算。我的是枚举起始点,开始运算。

我的滑动窗口对比官方滑动窗口

官方的思路是左边定死,右边移动,右边在移动的过程中,碰到重复的了,左边移动,再让右边移动看看还重复不重复了。知道产生了新的不重复,右边可以继续向下移动了。

我的思路和官方的思路一样的,就是我的代码冗余了很多。

官方滑动窗口的优化

i,j

j在向右走的时候,碰到了重复的,然后取得这个重复的index,下一次i直接跳到这个重复的index后即可。

我发现这个和我的动态规划思路是一样的

分析本质

本质是一个数组上的一片连续的区域。用滑动窗口来解决真的再合适不过。在右滑过程中,碰到重复的,需要找到重复的index,然后左边移到这个index的后面,继续右滑。

在这个过程中,记录最大值即可。

收获

get新名词,滑动窗口

猜你喜欢

转载自blog.csdn.net/qq_36523667/article/details/85225954