无重复字符的最长子串

无重复字符的最长子串

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

示例 1:

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一: 暴力循环

时间超时了

class Solution {
    public int lengthOfLongestSubstring(String s) {

        if(s.length()==0||s.length()==1){
            return s.length();
        }

        String result="";
        int maxLength =0;

        for(int j=0;j<s.length();j++){
            for(int i=j;i<s.length();i++){
            if(!result.contains(String.valueOf(s.charAt(i))) ) {
                result=result + String.valueOf(s.charAt(i));
                if(result.length()>maxLength){
                    maxLength=result.length();
                }
            
            }else{
                if(result.length()>maxLength){
                    maxLength=result.length();
                }
                result ="";
                 result=result + String.valueOf(s.charAt(i));
            }
           
        }
          if(result.length()>maxLength){
                    maxLength=result.length();
           }
          result ="";
           }
          return maxLength;
    }
}

方法二: 滑动窗口

什么是滑动窗口?

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

如何移动?

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

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

时间复杂度:O(n)

滑动窗口题目:

  1. 无重复字符的最长子串

  2. 串联所有单词的子串

  3. 最小覆盖子串

  4. 至多包含两个不同字符的最长子串

  5. 长度最小的子数组

  6. 滑动窗口最大值

  7. 字符串的排列

  8. 最小区间

  9. 最小窗口子序列

作者:powcai
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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;
        
    }
}

作者:powcai
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

框架 (伪代码)

class Solution:
    def problemName(self, s: str) -> int:
        # Step 1: 定义需要维护的变量们 (对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表)
        x, y = ..., ...

        # Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
        start = 0
        for end in range(len(s)):
            # Step 3: 更新需要维护的变量, 有的变量需要一个if语句来维护 (比如最大最小长度)
            x = new_x
            if condition:
                y = new_y

            '''
            ------------- 下面是两种情况,读者请根据题意二选1 -------------
            '''
            # Step 4 - 情况1
            # 如果题目的窗口长度固定:用一个if语句判断一下当前窗口长度是否达到了限定长度 
            # 如果达到了,窗口左指针前移一个单位,从而保证下一次右指针右移时,窗口长度保持不变, 
            # 左指针移动之前, 先更新Step 1定义的(部分或所有)维护变量 
            if 窗口长度达到了限定长度:
                # 更新 (部分或所有) 维护变量 
                # 窗口左指针前移一个单位保证下一次右指针右移时窗口长度保持不变

            # Step 4 - 情况2
            # 如果题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
            # 如果当前窗口不合法时, 用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
            # 在左指针移动之前更新Step 1定义的(部分或所有)维护变量 
            while 不合法:
                # 更新 (部分或所有) 维护变量 
                # 不断移动窗口左指针直到窗口再次合法

        # Step 5: 返回答案
        return ...

作者:eason734
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/yi-ge-mo-ban-miao-sha-10dao-zhong-deng-n-sb0x/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

另一种写法:

创建一个滑动窗口,i是左边界,j是右边界,创建一个哈希表,遍历字符串,如果这个字符不在哈希表中,说明现在这个窗口中不存在重复的元素,那么将这个元素的下标记录在哈希表中,否则如果这个字符出现过,并且还在i的后面,说明这个窗口中存在重复的元素,那么将窗口缩小成新的窗口,将i移动至这个元素之前出现的位置的下一个位置,并将哈希表记录这个元素的位置覆盖,并且每次循环判断这个窗口的大小是否为最大。

作者:yi-sheng-c3
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/by-yi-sheng-c3-7qy8/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> map;
        int i = 0,j = 0,ans = 0;
        while(j < s.size())
        {
            if (map.find(s[j]) != map.end()&&i <= map[s[j]]) // 如果这个窗口存在相同元素
                i = map[s[j]] + 1; // 缩小窗口
            if (j - i + 1 > ans) // 判断窗口的长度是否大于结果集
                ans = j - i + 1; // 记录结果
            map[s[j]] = j; // 覆盖元素位置
            j++;
        }
        return ans;
    }
};

作者:yi-sheng-c3
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/by-yi-sheng-c3-7qy8/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法三:双指针

如果不太理解 left = Math.max(left,map.get(s.charAt(i)) + 1); 可以使用双指针解决。

两个指针维护中间没有重复字符下标,右边指针每次移动一位,当加入当前字符产生重复时候,删除开头的字符,我们需要一个容器维护无重复的中间字符内容。由于英文字母数字符号空格 ASCII 码都在 128 内,随便用个数组记录维护即可。

作者:mochi-ds
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/-by-mochi-ds-lpxl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        //用来记录当前滑动窗口里字符出现的次数,维护没用重复字符的区间
        unordered_map<int, int>mp;
        for(int i = 0,j = 0; i < s.size(); i ++ )
        {
            //先把枚举的字符加入map如果当前i,j维护的区间内出现了重复的字符,则这个重复的字符一定是新加入的,因为我们i,j这两个指针维护的区间内是没有重复字符的
            mp[s[i]]++;
            //这一步就是移动j指针,每次移动把窗口里的字符删除知道找到和新加入的字符即重复的字符相同的j
            while(j < i && mp[s[i]] > 1)
            {
                mp[s[j]] --;
                j ++;
            }
            res = max(res, i - j + 1);
        }
        return res;
    }
};

作者:inspiring-newtonvxm
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/by-inspiring-newtonvxm-6t86/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

java版本

  if (s == null || s.length() == 0) {
            return 0;
        }

        // 无重复的最长子串的长度
        int res = 0;

        // 滑动窗口
        Map<Character, Integer> window = new HashMap<>();
        int left = 0;
        int right = 0;

        while (right < s.length()) {
            char newChar = s.charAt(right);
            // 扩大窗口
            right ++;
            // 处理窗口中新加入的元素 newChar
            window.put(newChar, window.getOrDefault(newChar, 0) + 1);

            // 如果窗口中该元素重复,则缩小窗口直至不重复
            while (window.get(newChar) > 1) {
                char removeChar = s.charAt(left);
                // 缩小窗口
                left ++;
                // 处理下窗口被移除的元素 removeChar
                window.put(removeChar, window.get(removeChar) - 1);
            }

            // 我们的窗口是左开右闭的,所以这里窗口的长度不要 +1 !!!
            res = Math.max(res, right - left);
        }

        return res;
    }

作者:beef98
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/leetcode-offer-tu-shuo-hua-dong-chuang-k-0wfj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法四:动态规划

维护每个字符第一次出现位置,那么如果当前字符没有出现过,长度为 i+1,如果出现过,那么最长长度为当前位置 i-last[s[i]]。维护最长长度即可。
这个思路是很多求最长/最短/无重复字串问题的思想。

作者:mochi-ds
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/-by-mochi-ds-lpxl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int> f(128, -1);
        int n = s.size(), start = -1, ans = 0;
        for(int i = 0; i < n; ++i) {
            start = max(start, f[s[i]]);
            ans = max(ans, i-start);
            f[s[i]] = i;
        } 
        return ans;
    }
};

作者:mochi-ds
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/-by-mochi-ds-lpxl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

java版本:

  public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) {
            return 0;
        }
        // 以s[i-1]结尾的最长无重复子串的开始下标
        int dp = 0;
        // 字符出现的最大的下标
        Map<Character, Integer> map = new HashMap<>();
        char[] charArr = s.toCharArray();
        int res = 0;
        for (int i = 0; i < charArr.length; i++) {
            Integer repeatIdx = map.get(charArr[i]);
            if (repeatIdx != null && repeatIdx >= dp) {
                dp = repeatIdx + 1;
            }
            res = Math.max(res, i - dp + 1);
            map.put(charArr[i], i);
        }
        return res;
    }

作者:Luke186
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/3-wu-zhong-fu-zi-fu-de-zui-chang-zi-chua-34ot/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

其他优化解法:

https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-4chong-jie/

https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/javade-6chong-jie-fa-by-sdwwld/
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

九城风雪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值