leetcode刷题(3)——无重复字符的最长子串

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

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

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

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

解题思路:
这道题主要用到思路是:滑动窗口

什么是滑动窗口?

其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!

如何移动?

我们只要把队列的左边的元素移出就行了,直到满足题目要求!

一直维持这样的队列,找出队列出现最长的长度时候,求出解!

刚开始的想法1:
刚开始想的是判断队列的首和尾是否和即将进来的元素有一致的,如果是首是一致的,就移除首,尾部一致,就将整个队列移除干净,于是有了如下的写法:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        ArrayDeque<Character> arrayDeque = new ArrayDeque<>();
        int maxSize = 0;
        for(int i=0;i<s.length();i++){
            Character charSet = s.charAt(i);
            if(arrayDeque.isEmpty()){
                arrayDeque.add(charSet);
            }else{
                Character firstChar = arrayDeque.peek();
                Character lastChar = arrayDeque.getLast();
                if(firstChar!=null&&firstChar.equals(charSet)){
                    arrayDeque.poll();
                
                }else if(lastChar!=null&&lastChar.equals(charSet)){
                    while(!arrayDeque.isEmpty()){
                        arrayDeque.poll();
                    }
                }
                arrayDeque.add(charSet);
            }
            maxSize = Math.max(maxSize,arrayDeque.size());
        }
        return maxSize;
    }
}

这个直接就错了,如果输入是"abcabcbb",我的写法输出4,正确结果是3,原因是没有考虑中间元素也可能和即将进来的元素会重合。

错误想法2:
决定用left和right来记录滑动窗口的左右边界,于是写了如下写法:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int left =0;
        int right=0;
        int max = 0;
        HashMap<Character,Integer> map = new  HashMap();
        for(int i=0;i<s.length();i++){
            Character charDex = s.charAt(i);
            if(!map.containsKey(charDex)){
                map.put(charDex,i);
            }else{
                int index = map.get(charDex);
                left = i;
                map.put(charDex,i);
            }
            right = i;
            max = Math.max(right-left+1,max);
        }
        return max;
    }
}

问题就是出在了left的更新上,这里left = i;的话,“dvdf"系统输出3,我输出2,如果是left=index+1;输入"abba”,系统输出2,我输出3, 所以左界限应该定义为 left = Math.max(left,index+1);,这里这个题目唯一难点

通用模版:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;
        
    }
}

耗时解法1:

public int lengthOfLongestSubstring(String s) {
        char[] chars = s.toCharArray();
        String pre = "";
        int index;
        int max = 0;
        LinkedHashMap<Character, Integer> linkedHashMap = new LinkedHashMap<>();
        ArrayDeque<Character> arrayDeque = new ArrayDeque<>();
        for (index = 0; index < chars.length; index++) {
            char charDex = chars[index];
            if (linkedHashMap.containsKey(charDex)) {
                int indexRepeat = linkedHashMap.get(charDex);
                for (Map.Entry<Character, Integer> entry : linkedHashMap.entrySet()) {
                    int value = entry.getValue();
                    Character key = entry.getKey();
                    if (value <= indexRepeat) {
//                        linkedHashMap.remove(key);
                        arrayDeque.push(key);
                    }
                }
            }
            while (arrayDeque.size() != 0) {
                Character character = arrayDeque.poll();
                linkedHashMap.remove(character);
            }
            linkedHashMap.put(charDex, index);
            max = Math.max(max, linkedHashMap.size());
        }
        return max;

    }

这个是我最初的解法,实在太耗时了,于是优化了一下:

public class Solution {
    public int lengthOfLongestSubstring(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;
    }
}

解法3:使用滑动窗口的模版
**注意:**之前滑动窗口的模版可以使用数组来作为 窗口,但是在这个问题上就能不使用了,因为这里没有说字符串只由26个字母组成,所以可能出现“ ”的字符串,必须使用map

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int left =0;
        int right=0;
        int max = 0;
        HashMap<Character,Integer> window = new HashMap();
        
        while(right<s.length()){
            char charDex = s.charAt(right);
            right++;
            window.put(charDex,window.getOrDefault(charDex,0)+1);
            while(window.getOrDefault(charDex,0)>1){
                char leftChar = s.charAt(left);
                window.put(leftChar,window.getOrDefault(leftChar,0)-1);
                left++;
            }
             max = Math.max(right-left,max);
        }
            
        return max;
    }
}

注意:这里的 max = Math.max(right-left,max);是放在while(right<s.length())这个循环中,因为要缩短了左边界,窗口长度固定以后,才开始比较大小

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的代码家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值