前言
本文将要和大家分享一个新的思路——滑动窗口,其实它也算是双指针的一种变形,下面将通过力扣hot100里的3. 无重复字符的最长子串来详细介绍一下。
思路分析
首先,得先了解一个概念,就是什么是子串,子串即为子字符串,它是原字符串中一段连续、非空的序列。明白了这点,就知道这题为什么用滑动窗口的思路了。
滑动窗口的核心是维护一个左闭右开的窗口 [left, right)(,保证窗口内的字符无重复。
滑动窗口的初始化:
left 初始化为 0,作为窗口的左边界。
right 从 0 开始遍历,作为窗口的右边界。
窗口的调整逻辑:对于每个 right,遍历 [left, right) 区间内的字符,检查是否存在与 s[right] 重复的字符:
若存在重复字符(下标为 i),则将 left 移动到 i + 1,保证新窗口 [i+1, right] 内无重复字符。
若不存在重复字符,则窗口自然向右扩展。
长度更新:每次调整窗口后,计算当前窗口的长度 right - left + 1,并与 maxlength 比较,更新最大值。
这种思路的时间复杂度为
O()(最坏情况下,如字符串所有字符都不重复时,内层循环会遍历整个窗口),空间复杂度为 O(1)(仅使用了几个整型变量)。
代码展示
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 用于记录最长无重复子串的长度
int maxlength = 0;
// 滑动窗口的左指针
int left = 0;
// 获取字符串的长度
int count = s.size();
// 滑动窗口的右指针,遍历字符串
for (int right = 0; right < count; right++) {
// 从左指针位置开始,检查到右指针前的字符,是否有与右指针字符重复的情况
for (int i = left; i < right; i++) {
if (s[i] == s[right]) {
// 若有重复,左指针跳到重复字符的下一个位置
left = i + 1;
break;
}
}
// 更新最长无重复子串的长度
maxlength = max(maxlength, right - left + 1);
}
return maxlength;
}
};
代码优化
这里采用了两层循环,当数据量大的情况下,时间成本较大,所以可以考虑将内层循环换成哈希表unordered_map,利用unordered_map查找的时间复杂度为O(1)的特性,从而大大提高时间效率(力扣提供的测试还不能体现哈希表的优势,所以优化后的代码运行效率可能还不如上面的代码),整体时间复杂度为 O(n),空间复杂度为 O(k)(k 是字符串中不重复字符的数量,最坏情况下 k=n)。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int maxlength = 0; // 记录最长无重复子串的长度
int left = 0; // 滑动窗口左边界
int count = s.size(); // 字符串长度
unordered_map<char, int> charIndex; // 哈希表:键为字符,值为该字符最后出现的下标
for (int right = 0; right < count; right++) {
char currentChar = s[right]; // 当前右指针指向的字符
// 若当前字符在哈希表中存在,且其最后出现的下标 >= left(说明在当前窗口内)
if (charIndex.find(currentChar) != charIndex.end() && charIndex[currentChar] >= left) {
// 将左指针移动到该字符最后出现位置的下一位,确保窗口内无重复
left = charIndex[currentChar] + 1;
}
// 更新当前字符在哈希表中的最新下标(无论是否重复,都要更新为当前right)
charIndex[currentChar] = right;
// 计算当前窗口长度,更新最大长度
maxlength = max(maxlength, right - left + 1);
}
return maxlength;
}
};
1769

被折叠的 条评论
为什么被折叠?



