【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

posted @   WilsonPan  阅读(169)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示