【探索-中级算法】无重复字符的最长子串

版权声明:转载请标明出处「OneDeveloper」 https://blog.csdn.net/OneDeveloper/article/details/83512075

在这里插入图片描述


1.

参考自:https://juejin.im/post/5aa159f86fb9a028bb189420

思路:
初始化一个 255 的 boolean 数组(字符对应的数字作为数组下标)作为所有可能出现的字符对应的存在可能性,不存在重复的均为 false,存在重复的,则对应的下标置为 true。

两个指针进行移动,前指针先不动,后指针移动并根据当前字符对应整数下标是否为 false 或者 true 进行判断。如果是 false,则表示没有重复,则指针向后移动一位;如果为 true,表示存在重复,则后指针停止移动,并计算当前字串长度,且将 boolean 数组重置,第一个指针向前移动一位,后指针指向当前前指针。

这样做其实就是普通的遍历,从字符串的第一个字母开始,得到以第一个字母开始的无重复的最大子串 s1,然后从字符串的第二个字母开始,得到以第二个字母开始的无重复的最大子串 s2,如此遍历,得到其中最长的。

public int lengthOfLongestSubstring(String s) {
    if (s == null || s.length() == 0) return 0;
    if (s.length() == 1) return 1;
    int max = 0;
    int firstPoint = 0;
    int nextPoint = 0;
    boolean[] exist = new boolean[255];
    int strLen = s.length();
    
    while (nextPoint < strLen) {
        int index = s.charAt(nextPoint);
        while (nextPoint < strLen && !exist[index]) {
            exist[index] = true;
            nextPoint++;
            if (nextPoint < strLen) {
                index = s.charAt(nextPoint);
            }
        }
        max = Math.max(max, nextPoint - firstPoint);
        
        if(max==len) return len;//如果最大无重复字串即为 s 自身,则直接返回
        ++firstPoint;
        nextPoint = firstPoint;
        for (int i = 0; i < 255; i++) {
            exist[i] = false;
        }
    }
    return max;
}

最坏情况时间复杂度为 O(n^2)

循环次数:n + (n-1) + ... + 1 = n*(n+1) /2  => O(n^2) 

空间复杂度为 O(1)


2.

参考自:
1、https://blog.csdn.net/weixin_38715903/article/details/80520727
2、https://juejin.im/post/5aa159f86fb9a028bb189420
3、https://blog.csdn.net/qq_17550379/article/details/80547777

利用滑动窗口的思想

思路:
以一个 hashmap 作为辅助,map 的 key 存储的是字符,value 存储的是该字符当前的位置,首先设置一个头指针,指向字符串开头,从最开始遍历字符串,如果 map 当中不包含这个字符,那么用这个字符当前所在的位置减去头指针的位置,然后与最大长度做比较,选取最大值成为最大长度,然后把当前字符以及位置放入 map。

如果当前字符已经存在于 map 中了,则表示出现了重复字符,那么把头指针移动到新重复字符处,即把之前的重复字符顶出去(即滑窗,当然这样做也会剔除掉原子串中之前那个重复字符前的多个非重复字符,就像下图中的第七次循环),用处于较后位置的重复字符代替,始终保持该字符不重复且处于子串中。

abcabcbb 为例:
在这里插入图片描述

public static int lengthOfLongestSubstring2(String s) {
    Map<Character, Integer> map = new HashMap<>();
    int maxLength = 0;
    int head = 0;
    for (int i = 0; i < s.length(); i++) {
        if (map.containsKey(s.charAt(i))) {
            head = Math.max(map.get(s.charAt(i)) + 1, head);
        }
        //(i - head + 1) 为 i 指向的字符距离头指针指向字符的长度
        if ((i - head + 1) > maxLength)
            maxLength = i - head + 1;
        map.put(s.charAt(i), i);
    }
    return maxLength;
}
head = Math.max(map.get(s.charAt(i)) + 1, head);

head当前字符上次出现位置的下一个字符的位置头指针原来指向的字符的位置 中的最大位置,考虑 abba 的情况,当 i = 3 时,map.get(s.charAt(3)) + 1 = 1, head = 2,因为当头指针从 s[0] 移动到 s[2]之后,没有及时清除掉 map 中的 ('a' -> 0),从而就影响到 map.get(s.charAt(i)) + 1 )。

这里时间复杂度为 O(n),空间复杂度为 O(1),map 数组也可用一个可能出现的字符数组代替(char[255],类似于第 1 点里面的)。

猜你喜欢

转载自blog.csdn.net/OneDeveloper/article/details/83512075