leetcode打卡10:题号3——无重复字符的最长子串。

题目:

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

示例 1:

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

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

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

题目解析一(暴力解法):

1.暴力解法其实就是不断的取子串进行判断是否有重复字符
2.所以标准答案给出了一个方案,定义一个allUnique(s,i,j)方法去判断在s中截取i到j的子串中是否存在重复
3.但是实际上这种做法非常的耗时,并且在leetcode上运行会报运行时间过长的问题

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int ans = 0;
        for (int i = 0; i < n; i++)
            for (int j = i + 1; j <= n; j++)
                if (allUnique(s, i, j)) ans = Math.max(ans, j - i);
        return ans;
    }

    public boolean allUnique(String s, int start, int end) {
        Set<Character> set = new HashSet<>();
        for (int i = start; i < end; i++) {
            Character ch = s.charAt(i);
            if (set.contains(ch)) return false;
            set.add(ch);
        }
        return true;
    }
}

题目解析二(自己的算法):

1.这个方法是我自己写出来的,写之前我也不知道其实这道题最优的解法是使用滑动窗口,但是我写出来之后发现我的这种方法集成了暴力算法和滑动窗口,虽然不如滑动窗口,但是比暴力解法好多了
2.设置两个初始化索引 i=0,j = i + 1,每次先将s【i】传入hashmap(实际上使用hashset就可以),之后开始从s【j】判断map中是否存在重复字符,如果不重复,就将s【j】传进去(其实就是判断从i到j的窗口中是否重复),然后j++
3.如果发现s【j】已经重复了,那么就记录窗口大小,然后 i++ ,j = i+1;这样就又重新开始了新的一遍遍历,第二次遍历就从i = 1, j=2,j开始往后走开始遍历。
在这里插入图片描述

public static  int lengthOfLongestSubstring(String s) {
        if(s.equals("") ) return 0;
        // if(s.length() == 0) return 0;
        int i = 0;
        int j = 0;
        int result = 1;
        for(i = 0; i < s.length(); i++){
            int temp = 1;
            HashMap map = new HashMap();
            map.put(s.charAt(i),i);
            for(j = i + 1; j < s.length(); j++){
                if(!map.containsKey(s.charAt(j))){
                    map.put(s.charAt(j),j);
                    temp = temp + 1;
                }else{
                    break;
                }
            }
            if(temp > result) result = temp;

        }
        return result;
    }

题目解析三(滑动窗口解法)

1.这个解法其实就是我自己写的方法的优化解法,我刚刚的解法还是可以优化的,具体优化的地方就在于,我每次的窗口是[i,j),每次走到重复的地方跳出,我又重新将窗口变成了[i+1,i+2),到这里我又要重新将map清空,然后又要从s【i+1】开始往map里面存值,但是实际上刚刚的清空的map中都会已经存有从s【i+1】开始的值,这样空间上和时间上都浪费了
2.所以我的窗口从[i,j)找到了重复的地方时,为了避免将set(这里使用set)清空,我将窗口直接变成[i+1,j),将set中的s【i】取出,这样的话就节省了我重新从s【i+1】开始存储的时间。
在这里插入图片描述

public int lengthOfLongestSubstring1(String s) {
        if(s.equals("") ) return 0;
        int i = 0;
        int j = 0;
        int length = s.length();
        int result = 0;
        HashSet set = new HashSet();
        while(i < length && j < length ){
            if(!set.contains(s.charAt(j))){ //无重复的就存储 区间右端点右移
                set.add(s.charAt(j));
                j += 1;
                result = Math.max(result,j-i);//取区间长度
            }else{//重复了 区间左端点右移

                set.remove(s.charAt(i));
                i += 1;
            }
        }
        return result;
    }

这里就比我刚刚的简单多了,省了每个新遍历的存储时间和判断时间。

题目分析四(最优化解法):

1.对于第三解法的滑动窗口解法如果已经弄懂了话,那么其实我们很清楚,当我们在[i,j)中找到了 s【j’ 】与 s【j】相同的话,我们其实可以直接将窗口定位到[j’+1,j),为什么这么做呢,因为 如果用上面的方法,[i,j’]中的元素我们还要一直和s[j]进行判断,不断的将区间左端点右移,直到移到j’+1,所以我们完全可以用空间换时间的想法,直接用索引定位 j’ 的位置,直接将区间定位到[j’+1,j)
2.但是在这里我们用的是map,并且不会进行擦除元素,也就是说当我们定位到[j’+1,j)之后,后面的字符在判断重复的时候可能会判断到区间之前的元素,这里要进行一个判断即可

 public int lengthOfLongestSubstring4(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>(); // current index of character
        // try to extend the range [i, j]
        for (int j = 0, i = 0; j < n; j++) {
            if (map.containsKey(s.charAt(j))) {
                i = Math.max(map.get(s.charAt(j)), i);//防止取到区间之前的元素
            }
            ans = Math.max(ans, j - i + 1);
            map.put(s.charAt(j), j + 1);
        }
        return ans;

    }

总结:
1.String字符串取长度的方法是length();
2.字符串判断为空串是用equals,用==判断不准,在我的IDEA中是可以的,但是在leetcode上不行
3.如果字符不为空,那么最小长度为1
4.hashset的判断是否存在方法为contains
5.取字符串的字符用charAt方法
6.map的get方法取到的是object对象,要进行强制转换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值