zoukankan      html  css  js  c++  java
  • 【LeetCode】3. 无重复字符的最长子串

    题目

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

    示例

    输入: "abcabcbb"

    输出: 3 

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

    解题

    一、暴力解法
    1. 循环每种可能出现的情况(二重循环)
    2. 取每组情况最大长度值
    public int LengthOfLongestSubstring(string s)
    {
        if (string.IsNullOrEmpty(s)) return 0;
    
        if (s.Length == 1) return 1;
    
        int max = 0;
        var hashSet = new HashSet<char>();
    
        for (int i = 0; i < s.Length; i++)
        {
            hashSet.Clear();
            hashSet.Add(s[i]);
    
            for (int j = i + 1; j < s.Length; j++)
            {
                if (!hashSet.Add(s[j])) break;
            }
            max = hashSet.Count > max ? hashSet.Count : max;
        }
        return max;
    }
    View Code

    二、分治法

    1. 将字符串分成两段str[0  ~ n/2] 和 str[n/2 + 1 ~ n],最长子串存在以下3种情况
      • 最长子串在str[0 ~ n/2]    
      • 最长子串在str[n/2+1 ~n]
      • 最长子串跨越在 str[0 ~ n/2] 和 str[n/2 +1 ~ n]
    2. 情况1,2,就取对应的字符串最长长度就可以  
    3. 情况3,若最长子串跨越两个字符串,必定是左右两个字符串重复出现的字符中间,所以左边扫描遇到重复停止,右边扫描遇到重复停止    

       

    public int LengthOfLongestSubstring(string s)
    {
        if (string.IsNullOrEmpty(s)) return 0;
    
        if (s.Length == 1) return 1;
    
        int lMax = 0, rMax = 0;
        int mid = s.Length / 2;
        var dic = new Dictionary<char, int>();
    
        for (int right = 0, left = 0; right < s.Length; right++)
        {
            if (dic.ContainsKey(s[right]))
            {
                left = Math.Max(dic[s[right]], left);  //标记左边为上一次位置,Max防止后面出现之前的
                if (left > mid) break;
            }
    
            lMax = Math.Max(lMax, right - left + 1);     //计算当前窗体长度(right - left + 1) 取最大值
            dic[s[right]] = right + 1;                 //更新当前字符位置
        }
    
        if (lMax >= s.Length) return lMax;
        dic.Clear();
    
        for (int right = s.Length - 1, left = s.Length - 1; left >= 0; left--)
        {
            if (dic.ContainsKey(s[left]))
            {
                right = Math.Min(dic[s[left]], right);  //标记左边为上一次位置,Max防止后面出现之前的
                if (right < mid) break;
            }
    
            rMax = Math.Max(rMax, right - left + 1);     //计算当前窗体长度(right - left + 1) 取最大值
            dic[s[left]] = left - 1;                     //更新当前字符位置
    
        }
        return Math.Max(lMax, rMax);
    }
    View Code

    三、滑动窗口

      什么是滑动窗口?在队列每次进入元素时判断是否符合条件,若不符合移除队尾数据,直到符合条件加入元素。

    1. HashSet存放字符
    2. 定义两个变量(i,j)从字符串头开始扫描
      •  若str[j] 不存在hashSet里,添加str[j]并且j++(向右移动)
      •  若str[j] 存在hastSet里,移除str[i]并且i++ (向左移动)
    public int LengthOfLongestSubstring(string s)
    {
        if (string.IsNullOrEmpty(s)) return 0;
    
        if (s.Length == 1) return s.Length;
    
        var hashSet = new HashSet<char>();
        int i = 0, j = 0, max = 0;
    
        while (i < s.Length && j < s.Length)
        {
            if (hashSet.Contains(s[j]))
            {
                hashSet.Remove(s[i++]);
            }
            else
            {
                hashSet.Add(s[j++]);
                max = Math.Max(hashSet.Count, max);
            }
        }
        return max;
    }
    View Code

    四、优化滑动窗口

      从上面可以知道,每次查找到存在的字符从开始移除,最坏的情况是每个字符都会访问两次,上面算法时间复杂度:O(2n)

    1. 循环过程记录字符的位置
    2. 遇到相同字符时,直接移动左边标记到上次字符出现位置
    3. 过程中记录每组最大长度(右边 - 左边)
    public int LengthOfLongestSubstring(string s)
    {
        if (string.IsNullOrEmpty(s)) return 0;
    
        if (s.Length == 1) return s.Length;
    
        int max = 0;
        var dic = new Dictionary<char, int>();
        for (int right = 0, left = 0; right < s.Length; right++)
        {
            if (dic.ContainsKey(s[right]))
            {
                left = Math.Max(dic[s[right]], left);  //标记左边为上一次位置,Max防止后面出现之前的
            }
    
            max = Math.Max(max, right - left + 1);     //计算当前窗体长度(right - left + 1) 取最大值
            dic[s[right]] = right + 1;                 //更新当前字符位置
        }
        return max;
    }
    View Code

      

      性能情况:方法一 < 方法二 < 方法三 < 方法四   

     来源:力扣(LeetCode)

    链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters

  • 相关阅读:
    JSON解析
    [Git的常见使用方式]
    [二进制]原码,反码,补码
    [伪] 级联菜单,两级菜单
    [心得]Java开发中的小心得
    [纪录]仿IOS滚轮效果(竖直滑动选择器)
    web测试总结
    科目组2
    科目组
    凭证:条目视图,总账视图
  • 原文地址:https://www.cnblogs.com/WilsonPan/p/11681873.html
Copyright © 2011-2022 走看看