无重复字符的最长子串

题目

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

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

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

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

请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

解题思路

当字符串为空或者长度为1时,直接返回字符串的长度

当字符串的长度大于1时,默认不重复字符串最大长度为1,采用两个指针 up 和 down 分别指向 字符数组的第一个、第二个位置

如果 up 和 down 指向的字符不相同,则 up 不动,down 向后移动,

否则,取出[up,down)之间的字符组成字符串,并同存储的不重复字符串最大长度比较,如果大于存储的不重复字符串最大长度,就更新其值,然后 down 指针不动,up向后移动,接着重复上述过程

移动过程中始终保持 down 指针 在 up指针的后面

代码

版本1.1

public class prac3 {
    public static void main(String[] args) {
        int len = lengthOfLongestSubstring("ckilbkd");
        System.out.println(len);
    }

    public static int lengthOfLongestSubstring(String s) {
        //如果字符串为空或为单个字符,直接返回字符串的长度
        if (s.length() <= 1) {
            return s.length();
        }
        //字符串的长度大于1的情况
        //将字符串转为字符数组
        char[] chars = s.toCharArray();
        //定义两个指针,up,down
        int up = 0;
        int down = 1;
        int max_str_len = 1;
        String split_str = "" + chars[0];
        while (down < s.length()) {
            if (!split_str.contains("" + chars[down])) {
                split_str = split_str + chars[down];
                down++;
            } else {
                String temp = getStr(chars, up, down);
                if(temp.length()>max_str_len){
                    max_str_len = temp.length();
                }
                up++;
                if(up==down){
                    down++;
                }
                split_str = getStr(chars,up,down);
            }
        }
        if(split_str.length()>max_str_len){
            max_str_len = split_str.length();
        }
        return max_str_len;
    }

    public static String getStr(char[] chars, int i, int j) {
        String str = "";
        for (int k = i; k < j; k++) {
            str += chars[k];
        }
        return str;
    }

}

版本1.2

public class prac3 {
    public static void main(String[] args) {
        int len = lengthOfLongestSubstring("pwwkew");
        System.out.println(len);
    }

    public static int lengthOfLongestSubstring(String s) {
        //如果字符串为空或为单个字符,直接返回字符串的长度("","d")
        if (s.length() <= 1) {
            return s.length();
        }
        //字符串的长度大于1的情况
        //将字符串转为字符数组
        char[] chars = s.toCharArray();
        //定义两个指针,up,down
        int up = 0;
        int down = 1;
        int max_str_len = 1;
        String split_str = "" + chars[0];
        while (down < s.length()) {
            if (!split_str.contains("" + chars[down])) {
                split_str = split_str + chars[down];
                down++;
            } else {
                String temp = getStr(chars, up, down);
                if(temp.length()>max_str_len){
                    max_str_len = temp.length();
                }
                //"abcdc":down指针指向第二个'c'时,发现了重复,为了避免重复比较,采用
                //此方式将 up指针 直接指向 'd'
                up = down - (temp.length() - temp.indexOf(chars[down])) + 1;
                //down指针和 up指针 有时会指向同一个字符
                // 采用此方式始终保持 down 在 up 的后面,例子:("aaabcd")
                if(up==down){
                    down++;
                }
                split_str = getStr(chars,up,down);
            }
        }
        //针对没有重复字符串的情况(abcde)
        if(split_str.length()>max_str_len){
            max_str_len = split_str.length();
        }
        return max_str_len;
    }

    public static String getStr(char[] chars, int i, int j) {
        String str = "";
        for (int k = i; k < j; k++) {
            str += chars[k];
        }
        return str;
    }

}

大佬代码学习

思路
  • 标签:滑动窗口

  • 暴力解法时间复杂度较高,会达到 O(n^2),故而采取滑动窗口的方法降低时间复杂度

  • 定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复

  • 我们定义不重复子串的开始位置为 start,结束位置为 end

  • 随着 end 不断遍历向后,会遇到与 [start, end] 区间内字符相同的情况,此时将字符作为 key 值,获取其 value 值,并更新 start,此时 [start, end] 区间内不存在重复字符

  • 无论是否更新 start,都会更新其 map 数据结构和结果 ans。

  • 时间复杂度:O(n)

  • 作者:guanpengchn
    链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/

    来源:力扣

代码示例
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;
    }
}

个人感受:感觉大佬这个算法还是挺好的,但是不好理解,我觉得存储字符下标更容易理解,在理解大佬的思想后,自己又写了一份代码,感觉确实优化了不少。

class Solution {
    public int lengthOfLongestSubstring(String s) {
       int len = s.length();
        int max_len =  0;
        HashMap<Character,Integer> map = new HashMap<>();
        for (int end=0,start=0; end < len; end++) {
            char alpha = s.charAt(end);
            if(map.containsKey(alpha)){
                //原先自己的思路是采用 start = map.get(alpha) + 1 来
                //更新start指针的位置,后来发现有问题
                //在测试字符串 "abba" 时,当 end 指向 最后一个 'a' 时,发现重复
                //于是将 start 更新为 第一个字符'a'的位置,这就增大了最大无重复字符串的长度
                start = Math.max(map.get(alpha) + 1,start);
            }
            map.put(alpha,end);
            max_len = Math.max(max_len,end - start + 1);
        }
        return max_len;
    }
}
  • 测试案例: "" "a" "aaaa" "aaabbw" "pwwkew" "abcabcbb" "abba"
  • 感觉力扣给出的测试案例还是挺好的,通过案例很容易发现问题并改进自己的代码,这道题感觉自己想复杂了,字符串长度完全可以通过两个指针来得到,自己却先将其截取,再求长度,使问题变得更复杂,HashMap真是好用,怪不得面试总爱问 hashMap 的 源码,受教了~

感想:第一次做力扣的算法题,感觉还是挺难的,一方面自己思路不清晰,另一方面容易少考虑情况,提交了好几次都是错的~,代码还是使用笨的方法实现的,这道题的类型属于滑动窗口,明天准备学习下大神的代码,并学习下左神算法的视频,以后要坚持刷算法题

原文地址:https://www.cnblogs.com/weixiao1717/p/14540862.html