[LeetCode-3] 无重复字符的最长子串

419 阅读2分钟

发布于个人公众号,打开微信,搜索MelodyJerry即可

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

难度中等通过率37.57%
(1106175/2943963)

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

示例 1:

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

示例 2:

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

示例 3:

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

示例 4:

输入: s = ""
输出: 0

提示:

  • 0 <= s.length <= 51045 * 10^4
  • s 由英文字母、数字、符号和空格组成

题解

① 滑动窗口

  • 滑动窗口字串问题基本都可以用滑动窗口来处理。

  • 什么是滑动窗口

    • 其实就是双指针的特殊用法

    • 通过左右指针维护一个动态的窗口

    • 通过判断后左指针滑动以实现缩小窗口右指针滑动以实现增大窗口

    • 进行窗口内数据的一系列更新操作

    • 循环操作,在特定条件下,更新答案

  • 滑动窗口,这是一套框架技巧思维

    • 这个技巧的时间复杂度O(n)O(n)
    • 远比字符串暴力要高效得多

时间复杂度:O(n)O(n)

空间复杂度:O(n)O(n)

② 遍历字符串

来自社区,作者:VioletKiss

遍历字符串,每次以 i 值记录,不回溯 i 值,用flag记录遍历过程找到的重复的字符的位置。如果遇到重复字符,i-flag 即为子串长度,此时flag重新定位到子串中重复字符的位置,i 继续往后遍历。这里length跟result记录长度。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() < 2) return s.length();
        int flag=0;//子串起始位置
        int max = 0;
        int len = 0;

        for (int i = 0; i < s.length(); i++) {
            int index = s.indexOf(s.charAt(i),flag);
            if(index < i){//从flag开始 出现了重复字符
                if(max<len){//记录len
                    max = len;
                }
                flag = index+1;//记录子串起始位置
                len = i-flag+1;//重新计算字串长度
            }
            else{
                len++;
            }
        }
        return len>max?len:max;//若最后一个子串没有出现重复字符就需要判断
    }
}
class Solution {
    public int lengthOfLongestSubstring(String s) {
        //a b c a b c d a d e
        //0 1 2 3 4 5 6 7 8 9
        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;
    }
}