LeetCode刷题第3题【无重复字符的最长子串
】—解题思路及源码注释
结果预览
一、题目描述
给定一个字符串 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、问题理解
我们需要在给定的字符串 s 中找到最长的不包含重复字符的子串的长度。
子串是连续的字符序列,因此我们需要通过优化的方式找到在字符不重复的前提下,能获取到的最长连续子串的长度。
2、解题思路
这个问题的最佳解法是 滑动窗口(Sliding Window) 技巧,基本步骤如下:
1、滑动窗口定义:
1> 我们使用两个指针 left 和 right 来表示当前窗口,窗口内的子串是我们需要检查的部分。left 为窗口的左边界,right 为右边界。
2> right 指针用于扩展窗口,而 left 指针则在遇到重复字符时收缩窗口,确保窗口内没有重复字符。
2、窗口扩展和收缩:
right 指针从字符串的左端开始,逐个字符扩展窗口。如果 right 指向的字符在当前窗口内已经出现过,那么就需要通过移动 left 指针来收缩窗口,直到窗口内没有重复字符。
3、字符位置映射:
为了避免频繁的删除操作,我们使用一个数组 index_map 来记录每个字符最后一次出现的位置。当 right 指针遇到重复字符时,可以直接通过 index_map 得知 left 指针应该移动到哪一位置,避免了逐一收缩的效率瓶颈。
4、最大子串长度更新:
每次扩展窗口后,我们更新当前窗口的大小 right - left + 1,并与已记录的最大长度进行比较,保留较大的值。
5、时间复杂度:
由于每个字符最多被访问两次,时间复杂度为 O(n),其中 n 是字符串的长度。
6、空间复杂度:
空间复杂度为 O(1),我们仅使用了一个大小为 128 的数组 index_map,来记录字符的最后位置(假设字符集为 ASCII)。
三、代码实现及注释
1、源码实现
class Solution {
public:
int lengthOfLongestSubstring(std::string s) {
// 记录每个字符最后一次出现的索引
std::vector<int> index_map(128, -1); // 假设字符集是 ASCII
int left = 0; // 左指针
int max_len = 0; // 存储最长子串的长度
// 右指针从0到n遍历字符串
for (int right = 0; right < s.length(); ++right) {
// 如果当前字符已经出现过,并且索引大于等于left
// 则需要将左指针移动到该字符的下一位置
if (index_map[s[right]] >= left) {
left = index_map[s[right]] + 1;
}
// 更新当前字符的位置
index_map[s[right]] = right;
// 更新最长子串长度
max_len = std::max(max_len, right - left + 1);
}
return max_len;
}
};
2、代码解释
1、字符位置映射 index_map:
我们使用一个大小为 128 的数组 index_map 来记录每个字符在字符串中最后一次出现的位置(假设字符集是 ASCII)。如果字符 c 出现过,我们就记录它的索引位置。
2、左右指针 left 和 right:
left 是窗口的左边界,right 是窗口的右边界。right 指针从字符串的左端开始,遍历字符串的每个字符。
3、收缩窗口:
当 right 指向的字符在当前窗口内已经出现时,意味着我们遇到了重复字符。此时我们需要移动 left 指针来收缩窗口,直到窗口内不再包含重复字符。通过 index_map[s[right]] 可以知道该字符最后出现的位置,因此我们可以直接将 left 移动到该字符位置的下一位,从而跳过一些不必要的字符。
4、更新最大子串长度:
每次扩展窗口时,我们计算当前窗口的长度 right - left + 1,并与已记录的最大子串长度 max_len 进行比较,保留较大的值。
四、执行效果
1、时间和空间复杂度分析
时间复杂度: O(n)
其中 n 是字符串的长度。每个字符最多被 left 和 right 指针访问两次,因此时间复杂度为 O(n)。
空间复杂度: O(1)
由于我们使用一个大小为 128 的数组 index_map 来记录字符的最后位置,这个数组的大小是常数,因此空间复杂度为 O(1)。