鱼C论坛

 找回密码
 立即注册
查看: 70|回复: 4

[技术交流] 3. 无重复字符的最长子串

[复制链接]
发表于 前天 16:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Gnomeshgh 于 2025-8-18 16:42 编辑

一、题目描述

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



示例 1:

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

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

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


提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成


二、解题思路

(一)暴力解

常规思路遍历所有的子串,并判断该字串中是否包含重复的元素。
首先使用两层循环,外层循环代表字串的起始位置,内层循环判断字串中是否有重复的元素。
使用集合来进行上述操作,首先判读元素是否在集合中,是则结束该子串的判断,否则加入到集合中。


  1. class Solution:
  2.     def lengthOfLongestSubstring(self, s: str) -> int:
  3.         # 最后返回的最大字串长度
  4.         max_len = 0
  5.         # 获取字符串的长度
  6.         strlen = len(s)

  7.         # 外层循环记录字串的起始位置
  8.         for i in range(strlen):

  9.             # 当剩余字串的长度无法大于最大子串长度就直接结束循环
  10.             if max_len > strlen - i:
  11.                 break
  12.             
  13.             # 设置一个集合
  14.             strset = set()
  15.             # 存储当前子串的长度
  16.             length = 0
  17.             
  18.             # 内层循环进行子串是否有重复元素的检测
  19.             for j in range(i,strlen):
  20.                 # 该元素不在,则加入到集合中,并且当前子串长度加1,并更新最大子串长度
  21.                 if s[j] not in strset:
  22.                     strset.add(s[j])
  23.                     length = length + 1
  24.                     max_len = max(max_len , length)
  25.                 # 该元素在集合中,就结束该子串的判断,开启下一个子串的判断
  26.                 else:
  27.                     break
  28.         return max_len</font>
复制代码

(二)滑动窗口

对暴力解进行优化,举例子


wechat_2025-08-18_162009_170.png

可以看出,每次集合中存在已有的元素,就进行下次循环,在进行下次循环的时子串的起始位置后移一位。然后再次从起始位置开始新的循环。

其中bc很明显是重复操作了。


在第二个a插入之前,集合中是没有重复元素的,结束该子串后,只需要将首位后移一位即可,此时也删除p_front所指元素,因为在第二个a插入之前,集合不存在重复的元素。
然后进行第二个a的判断。

总结一下就是,第一个指针指向子串的首部,第二个指针指向子串的尾部,不断的将尾部插入到集合中,
若插入集合已存在该元素,首先删除p_front所指元素,然后p_front后移一位,尾指针不变。然后依次循环。

  1. class Solution:
  2.     def lengthOfLongestSubstring(self, s: str) -> int:
  3.         # 获取字符串长度
  4.         s_len = len(s)
  5.         # 头指针
  6.         p_front = 0
  7.         # 尾指针
  8.         p_rear = 0
  9.         # 记录子串最大值
  10.         max_len = 0
  11.         # 设计一个空集合
  12.         str_set = set()

  13.         # 当尾指针大于字符串长度结束循环
  14.         while p_rear < s_len:
  15.             # 当元素不在集合中插入到集合中,否则头指针后移一位,且删除该元素,删除的是p_front所指的元素,而不是重复的元素
  16.             if s[p_rear] not in str_set:
  17.                 str_set.add(s[p_rear])
  18.                 p_rear = p_rear + 1
  19.                 max_len = max(max_len , p_rear - p_front)
  20.             else:
  21.                 str_set.discard(s[p_front])
  22.                 p_front = p_front + 1
  23.         return max_len
复制代码


再次进行优化

wechat_2025-08-18_162728_167.png



可以看出,w是重复的元素,且还知道第一个w的下标,可以直接移动到第一个w的后一位,这样就可以减少p_front的多次移动,在这个过程中,要删除小于等于第一个w下标的元素。
怎么快速知道元素下标那,可以使用哈希表进行完成。使用字典来实现哈希表。
在p_front移动的时候,p_front之前的元素全部无效,因为前面的已经不是子串了。
总结一下就是,判断元素在不在哈希表中,不在则更新哈希表。在则判断是否是有效的(有效就是大于p_front),有效的就更新p_front,且更新哈希表,无效也更新哈希表,
也就是无论怎么样都要更新哈希表。


  1. class Solution:
  2.     def lengthOfLongestSubstring(self, s: str) -> int:
  3.         # 哈希字典:用于存储每个字符最后一次出现的索引位置
  4.         str_dict = {}
  5.         # 记录最长无重复子串的长度,初始化为0
  6.         max_len = 0
  7.         # 左指针:标记当前窗口的起始位置,初始化为0
  8.         p_front = 0
  9.         
  10.         # 右指针遍历字符串,enumerate同时获取索引(p_rear)和字符(char)
  11.         for p_rear, char in enumerate(s):
  12.             # 核心判断:如果字符已存在于字典中,且上次出现的位置在当前窗口内(>=左指针)
  13.             # 说明出现重复,需要移动左指针到重复字符的下一位,确保窗口内无重复
  14.             if char in str_dict and p_front <= str_dict[char]:
  15.                 p_front = str_dict[char] + 1
  16.             
  17.             # 更新当前字符在字典中的最后出现位置为右指针索引
  18.             str_dict[char] = p_rear
  19.             
  20.             # 计算当前窗口长度(右指针-左指针+1),并更新最大长度
  21.             max_len = max(max_len, p_rear - p_front + 1)
  22.         
  23.         # 返回最长无重复子串的长度
  24.         return max_len
复制代码

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 前天 16:16 | 显示全部楼层
要解决“无重复字符的最长子串”问题,可以使用**滑动窗口**算法,通过双指针和哈希表高效地跟踪不重复字符的窗口。以下是详细思路和代码:

### 解决思路
1. **初始化**:
   - 左指针 `left = 0`,记录窗口起点。
   - 哈希表 `char_map` 存储字符最近一次出现的索引。
   - `max_len` 记录最大无重复子串长度。

2. **遍历字符串**(右指针 `right` 从 `0` 开始):
   - 若当前字符 `s[right]` 已存在于 `char_map` 中,**且其上次出现位置在 `left` 右侧**(即在当前窗口内),则将左指针移动到重复字符的下一位:`left = max(left, char_map[s[right]] + 1)`。
   - 更新 `char_map` 中 `s[right]` 的索引为当前 `right`。
   - 计算当前窗口长度 `right - left + 1`,并更新 `max_len`。

3. **返回结果**:
   - 遍历结束后,`max_len` 即为最长无重复子串的长度。

### 代码实现

  1. def lengthOfLongestSubstring(s: str) -> int:
  2.     if not s:
  3.         return 0
  4.     char_map = {}  # 存储字符最后出现的位置
  5.     left = 0       # 滑动窗口左边界
  6.     max_len = 0    # 记录最大长度
  7.    
  8.     for right in range(len(s)):
  9.         # 如果字符已存在且在当前窗口内,移动左指针
  10.         if s[right] in char_map:
  11.             left = max(left, char_map[s[right]] + 1)
  12.         # 更新字符的最近位置
  13.         char_map[s[right]] = right
  14.         # 更新最大长度
  15.         current_len = right - left + 1
  16.         if current_len > max_len:
  17.             max_len = current_len
  18.     return max_len
复制代码


### 示例解析
s = "abcabcbb" 为例:
1. right=0:字符 'a' 加入,窗口为 "a",长度=1。
2. right=1:字符 'b' 加入,窗口为 "ab",长度=2。
3. right=2:字符 'c' 加入,窗口为 "abc",长度=3(最大长度)。
4. right=3:字符 'a' 重复,左指针移动到 1,窗口为 "bca",长度=3。
5. 后续同理,最终返回最大长度 3。

### 复杂度分析
- **时间复杂度**:O(n),仅需一次遍历字符串。
- **空间复杂度**:O(字符集大小),哈希表存储字符索引。

此方法高效且简洁,适用于所有符合题意的输入。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 前天 16:39 | 显示全部楼层
有点难!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 前天 17:32 | 显示全部楼层
我这个怎么样
  1. int maxlen(string s) {
  2.         unordered_map<char, int> hash;
  3.         int n = s.length();
  4.         int res = 0;
  5.         int l = 0;//左指针
  6.         for (int r = 0; r < n; r++) {//右指针
  7.                 hash[s[r]]++;
  8.                 while (hash[s[r]] > 1) {//出现重复字符
  9.                         hash[s[l]]--;
  10.                         l++;//右移左指针
  11.                 }
  12.                 res=max(res,r-l+1);//记录最大值

  13.         }
  14.         return res;
  15. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 前天 21:29 | 显示全部楼层

你这个也很巧妙,而且还非常简洁,while还可以在优化一下,可以直接跳到重复的下标的后一位,这样不用每次都判断了。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-8-20 16:07

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表