《LeetCode刷题》3.无重复字符的最长子串(java篇)

题目描述:

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

示例 1:

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

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

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

---------------------------------------------------------------------------------------------------------------

day1:耗时半小时,通过率超过18%,慢慢找寻自己数据结构方面不足,提升思维

我的代码:

public static int lengthOfLongestSubstring(String s) {
        String longStr = "", tempStr = "";
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            int num = tempStr.indexOf(c);
            if (num == -1){
                tempStr += c;
            }else {
                tempStr = tempStr.substring(num+1)+c;
            }
            if (tempStr.length() > longStr.length()){
                longStr = tempStr;
            }
        }
        return longStr.length();
    }

思路:通过longStr记录当前最长子串,tempStr用来存放当前子串,for循环获取字符串中每个字符,通过indexOf方法比较当前字符是否存在于tempStr中,不在当前字符串中时把当前字符附加到字符串后(这里建议用StringBuilder),在当前字符串中则把tempStr中的当前字符以后的所有字符保存为新字符串,最后通过比较tempStr和longStr的长度得到最长的子串。(ps:笔者知道用哈希会更好算,但是忘了使用哈希的方式了,就采用了最基本的思路来做)


官方思路:(哈希表)

public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set occ = new HashSet();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                // 不断地移动右指针
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }

思路解析(个人思路,仅供参考):生成一个空集合,用来记录最长子串,通过for循环遍历字符串,每循环一次,删除集合中的一个字符(因为本题只要求求出最长长度,所以这里删除集合中的字符也无所谓),通过一个while循环,将字符串中的字符添加到集合中去,while判断条件为不超出字符长度,并且下一个字符不存在于集合中,rk用来表示字符串中记录的下标(这里while的判断比较关键,一边记录下标,一边判断下一个字符是否在集合中,当下一个集合已经在集合中时,需等待到上面的occ.remove删除这个字符后,才会再次添加到集合中,并记录下标),ans用来记录最长的子串,通过比较当前ans和下标减去循环数的大小来确定是否更新ans值,最后返回ans值


大佬思路:(狂吹大佬,六批六批)

《LeetCode刷题》3.无重复字符的最长子串(java篇)_第1张图片

public int lengthOfLongestSubstring(String s) {
        // 记录字符上一次出现的位置
        int[] last = new int[128];
        for(int i = 0; i < 128; i++) {
            last[i] = -1;
        }
        int n = s.length();

        int res = 0;
        int start = 0; // 窗口开始位置
        for(int i = 0; i < n; i++) {
            int index = s.charAt(i);
            start = Math.max(start, last[index] + 1);
            res   = Math.max(res, i - start + 1);
            last[index] = i;
        }

        return res;
    }

思路解析:(个人看法,仅供参考)

通过设置一个128的数组(因为ascii码只有128位,即所有字符都能存放进去),for循环用来把数组中的所有值初始化为-1(这里不初始化使用默认值0也可以,把后面值域改一下就可以),n用来表示字符串的长度,res用来记录最长子串的长度,start表示当前字符是否存在,如果存在那么下标值则记录为start,for循环字符串长度,用index获取当前字符的ascii码,(start和res这两句很妙)start的值为start和last[index]+1(这里加一是因为默认值为-1)中最大的一个,start默认值为0,last[index]+1的默认值也为0,所以只有当index第二次出现时,last[index]+1必大于0(因为last[index]=i,而i是大于等于0的数),且此时start为第二次出现的下标值(很关键),res的值为res和(i-start+1)的最大值,res默认值为0,i在一直加1,所以当start为0时,每次res会增加1,即表示为这个数没出现过,那么当前下标为几,则当前最长子串的数值就为几,当start不等于0后,因为表示为上一次出现这个字符的下标,则res要等待start轮后才能再继续增长(这里比较绕,可以多读一下理解理解)

举个例子:比如字符串:abdac,第一轮last[a] = 0,start = 0,res = 1;第二轮last[b] = 1,start=0,res=2;第三轮同理,第四轮时下标为a,当前last[a]已经有值了,为0,所以start=max(0,1)=1(这里1表示a在当前存储的字符串中的位置是第一个,也代表只空一轮,则下一轮会继续增长,如果这里是2,则表示会空两轮,则从第六轮才会继续增长),res=max(3,3-1+1)=3,第五轮last[c]=4,start=1,res=4,即最长为4,bdac

总结:自己数据结构方面思路比较浅显,常规思路也想不到很高深的,只有定个小目标,每天坚持一个及以上算法,提升思维,提高代码能力。最后,坚持写博客(虽然已经很久没写了)。

你可能感兴趣的:(java,leetcode,算法,算法,leetcode,java,数据结构)