LeetCode第三题:无重复字符的最长子串(Java)

题目:无重复字符的最长子串

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

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

方法一:自己想出来的

自己想出来的思路,消耗内存和执行用时都不怎么乐观。应该还可以优化,有时间再修改。

思路:

         把第一个元素设为子串,和字符串的下一位比较,子串中没有相同元素则把该元素添加到子串中。如果有相同元素,记录当前子串的长度,则把子串中从头直至相同元素排出,再次比较子串是否有和下一位相同的元素。直至整个字符串都添加过子串中。最大的字串长度则是符合题意的最长字串的长度。可能描述的不清楚,可以看下面的示例

       例如:字符串abcabcbb 括号表示子串

    (a)bcabcbb    b和子串(a)里没有相同元素,把b添加到子串中

    (ab)cabcbb    c和子串(ab)里没有相同元素,把c添加到子串中

     (abc)abcbb   a和子串(abc)里相同元素,当前子串长度为3,把子串里的直至相同元素排出去,并把相同元素添加进子串。现子串(bca)

     a(bca)bcbb   b和子串(bca)里相同元素,当前子串长度为3,把子串里的直至相同元素排出去,并把相同元素添加进子串。现子串(cab)

     ab(cab)cbb   c和子串(cab)里相同元素,当前子串长度为3,把子串里的直至相同元素排出去,并把相同元素添加进子串。现子串(abc)

     abc(abc)bb   b和子串(abc)里相同元素,当前子串长度为3,把子串里的直至相同元素排出去,并把相同元素添加进子串。现子串(cb)

     abcab(cb)b   b和子串(cb)里相同元素,当前子串长度为2,把子串里的直至相同元素排出去,并把相同元素添加进子串。现子串(b)

     abcabcb(b)   当前字串长度为1,循环结束。

       所以最长字串长度为3

public static int lengthOfLongestSubstring(String s) {
        if (s.length() <= 0 || s == "" || s == null) {
            return 0;
        }
        //下标
        int index= 0;
        //记录当前子串的长度
        int a = 1;
        //记录最大长度
        int ans=0;
        for (int i = 0; i < s.length() -1; i++) {
            String str = s.substring(index, i + 1);
            //子串和下一位不相等
            if (str.indexOf(s.charAt(i + 1)) == -1) {
                a ++;
            }else{
                //排除子串中直至相同元素后,新子串第一位的索引
                index =str.indexOf(s.charAt(i + 1))+ 1 +index;
                //记录最大的长度
                if (a> ans) ans = a;
                //当前子串的长度,+1是把相同元素添加进子串后计算长度
                a =s.substring(index,i+1).length()+1;

            }
        }
        if (a> ans) ans = a;
        return ans;
    }

消耗内存和执行用时多,应该还可以优化(减少循环?),有时间再弄。

 

方法二:官方给的方法(滑动窗口)

找出每个字符开始的,不包含重复元素的最长字串

设置一个HashSet判断是否有重复元素,没有重复元素就一直往里面添加。遇到重复元素移除HashSet里第一个元素,如果依旧重复就继续移除,直至不重复就继续添加。每次添加记录HashSet长度,最大的长度就是本题所求。

    public static int lengthOfLongestSubstring(String s) {
        Set<Character> hashSet = new HashSet<>();
        //右指针,不断添加
        int right = -1;
        int ans = 0;
        //i可以看作左指针,从左边移除
        for (int i = 0; i < s.length(); i++) {
            //i为0时HashSet为空不用移除
            if (i != 0) {
                hashSet.remove(s.charAt(i - 1));
            }
            //字符串的下一位不与HashSet里面元素相同,添加进hashSet,右指针向右移动。
            //遇到相同元素,结束本次小循环,进大循环(移除下标i的元素,左指针+1(i+1)。
            //即HashSet集合中第一位元素变成下标为i+1的元素
            while (right + 1 < s.length() && !hashSet.contains(s.charAt(right + 1))) {
                hashSet.add(s.charAt(right + 1));
                right++;
            }
            //遇到有重复元素,则记录最大长度
            ans = Math.max(ans, right - i + 1);
        }
        return ans;
    }

示例:字符串abcabcbb    ()里为HashSet集合里面的元素。

(a)bcabcbb  (ab)cabcbb   (abc)abcbb    i=0时,不断添加,当right=2时,charAt(right+1) 为HashSet里重复元素。

a(bc)abcbb   a(bca)bcbb    i=1时,移除重复元素charAt(i-1),再次不断添加。当right=3时,charAt(right+1) 为HashSet里重复元素。

ab(ca)bcbb    ab(cab)cbb   i=2时

abc(ab)cbb   abc(abc)bb    i=3时

abca(bc)bb                         i=4时,hashSet里元素依旧存在下一位元素charAt(right+1)  right为5 相同,结束本次小循环,开始新一轮大循环。

abcab(c)bb  abcab(cb)b     i=5时 

abcabc(b)b                         i=6时

abcabcb()b   abcabcb(b)    i=7时

 

方法三:暴力求解

记录所有不重复子串的长度,找到最大的长度即为题目所求。

 public static int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int ans = 0;
        Set<Character> set = new HashSet<>();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                String str = s.substring(i, j);
                char[] chars = str.toCharArray();
                for (char c : chars) {
                    set.add(c);
                }
                //相等意味着str没有重复元素
                if (set.size() == str.length()) {
                    ans = Math.max(ans, str.length());
                }else{
                    //已经存在重复元素,j再怎么变化依旧存在重复元素,直接结束这个循环
                    set.clear();
                    break;
                }
            }
        }
        return ans;
    }

HashSet不存放重复元素,所有元素存放到HashSet中,如果HashSet长度和字符串长度不相同,就意味着有重复元素(HashSet没存放那个重复元素,所以导致长度不同)。

 

方法四:评论区 画解算法的方法 改进版

map集合,存放字符和字符最后一次出现的位置。

每次出现相同元素就把start移动到 start和end中相同元素出现的 下一位

    public static int lengthOfLongestSubstring(String s){
        int n = s.length();
        int ans= 0;
        HashMap<Character, Integer> map = new HashMap<>();
        for (int start = 0,end = 0; end<n;end++){
            char c = s.charAt(end);
            if(map.containsKey(c)){
                //相同字符出现位置的下一位,start不能比当前的start小
                start = Math.max(map.get(c)+1,start);
            }
            map.put(c,s.indexOf(c));
            ans=Math.max(ans,end-start+1);
        }
        return ans;
    }

start = Math.max(map.get(c)+1,start)语句 的作用

例如字符串 pwpwewp,最后一次循环start再‘e'位置,end再‘p’位置。如果没有这句start将回到‘p’上一次出现位置的下一位,即索引3。我们可以看到子串wewp不符合条件。预防这种情况,start左移动,所以要添加这一句。

 

方法五:评论区看到的一种方法

    public static int lengthOfLongestSubstring(String s){
        int maxSize = 0;
        //记录ASCII 码字符出现的位置,以字符作为下标
        int[] dict = new int[128];
        //为了方便理解,这里把数组内容全部设为 -1,之后在记录的时候就可以从 0 开始,方便理解
        Arrays.fill(dict, -1);
        //用于记录重复 ASCII 码字符出现的位置的值
        int repeatValue = -1;
        // 当前下标
        int i = 0;
        //存放左下标
        int ASCII;
        while (i < s.length()) {
            ASCII = s.charAt(i);
            //如果当前位置的值 > repeatValue,证明当前位置已经赋过一次值了,证明字符重复
            if (dict[ASCII] > repeatValue)
                //更新 repeatValue 为之前赋值的下标
                repeatValue = dict[ASCII];
            //将当前下标赋值到数组相应位置
            dict[ASCII] = i;
            //i - repeatValue(去除重复部分)
            // 比如 abcabcdade 中的三个 a 的计算  abca - a(3 - 0)=bca   abcabcda - abca(7 - 3)=bcda
            maxSize = Math.max(maxSize, i - repeatValue);
            //s.length() - repeatValue - 1 判断剩下的数有没有必要继续循环
            //比如 abcabcdade 最后的 a(当 i = 7 repeatValue = 3) ,abcabcdade - abca(10-3-1) = bcdade  剩下最多有六位
            //比如 abcabcdade 最后的 d(当 i = 8 repeatValue = 6) ,abcabcdade - abcabcd(10-6-1) = ade  剩下最多也是三位
            if (maxSize >= s.length() - repeatValue - 1) {
                return maxSize;
            }
            i++;
        }
        return maxSize;

    }

和第四种方法差不多,用数组记录最后出现一次的下标。int[128],大多数字符都在ASCII前。128足够用了

 

学习到的东西:

 set也可以判断字符串是否含有重复元素。相比两次循环每个元素都比较更优。

滑动窗口:左指针固定,右指针不断右移。不满足条件时右指针固定,左指针右移动一位。

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值