LEETCODE的第三题——无重复字符的最长子串(滑动窗口)
本文主要用于记录刷力扣的题解,想通过深度学习力扣的每一道题目的原理来提高自己。
下面内容为自己学习时的记录,如有错误,欢迎指正🍭
二、思路方法
2.1 滑动窗口法
- 使用两个指针,start 和 end,来维护一个滑动窗口,表示当前考察的子串。
- 遍历字符串,将字符加入窗口,如果窗口内有重复字符,移动 start 指针直到窗口内没有重复字符。
- 在遍历过程中,不断更新最大子串长度。
这种方法的时间复杂度为 O(n),其中 n 是字符串的长度。
滑动窗口
滑动窗口法是一种用于解决字符串或数组相关问题的常用技巧。它通过维护一个可变大小的窗口来遍历字符串或数组,以解决特定问题。这个窗口通常表示一个子数组或子字符串,可以滑动(移动)以适应问题的要求。
下面是滑动窗口法的一般步骤和解释:
-
定义窗口的起始和结束位置:首先,定义两个指针,通常称为窗口的起始位置和结束位置。这两个指针确定了当前窗口的大小。
-
移动窗口:根据问题的要求,移动窗口的起始或结束位置。通常,窗口会向右移动一个单位,但也可以根据需要进行其他类型的移动。移动窗口可能包括增加结束指针、减少起始指针、或两者同时进行。
-
检查窗口内容:在每次移动窗口后,检查窗口内的内容,通常是子数组或子字符串,以满足问题的条件。这可以是查找最小值、最大值、计算总和、查找特定模式等等。
-
更新最终结果:根据问题的要求,在每次移动窗口后更新最终结果。可能需要记录满足条件的子数组或子字符串的长度、内容、数量等等。
-
继续遍历:重复步骤2至步骤4,直到遍历完整个字符串或数组。
滑动窗口法的优点在于它可以在线性时间内解决许多与子数组或子字符串有关的问题,而不需要显式地生成所有可能的子数组或子字符串,从而节省了时间和空间。
一些常见的问题类型,可以使用滑动窗口法来解决,包括:
查找最长子字符串
,该子字符串满足特定条件(例如,不包含重复字符的最长子字符串)。查找包含特定元素或模式的子数组或子字符串
。查找最小窗口
,使其包含所有给定元素的子数组或子字符串。计算子数组或子字符串的总和、平均值等数值属性
。
总之,滑动窗口法是一种强大的解决问题的工具,可以有效地解决许多与子数组或子字符串相关的问题,其核心思想是通过动态维护窗口来满足特定条件。
2.2 哈希表法
- 使用哈希表来存储字符及其出现的索引。
- 遍历字符串,如果字符不在哈希表中,将字符和索引添加到哈希表中;如果字符已经在哈希表中,更新 start 指针为重复字符的下一个位置,并更新最大子串长度。
这种方法的时间复杂度同样为 O(n)
哈希表相关可点击
三、代码实现
3.1 滑动窗口法(最容易想到)
1) python
def lengthOfLongestSubstring(s: str) -> int:
max_length = 0 # 最长子串的长度
start = 0 # 滑动窗口的起始位置
char_set = set() # 集合,用于存储当前子串中的字符
for end, current_char in enumerate(s):
# 如果字符已经在当前子串中出现,将窗口起始位置移动到重复字符的下一个位置
while current_char in char_set:
char_set.remove(s[start])
start += 1
# 将当前字符添加到集合中
char_set.add(current_char)
# 计算当前子串的长度并更新最大子串长度
current_length = end - start + 1
max_length = max(max_length, current_length)
return max_length
解释:以下是代码中各个变量的含义注释:
max_length
: 用于存储最长子串的长度,初始化为0。start
: 滑动窗口的起始位置,初始化为0。char_set
: 一个集合,用于存储当前子串中的字符,初始为空集合。
代码通过遍历字符串s
中的每个字符,逐步构建和更新当前子串,同时更新max_length
的值。
具体步骤如下:
- 在每次循环中,检查当前字符
current_char
是否已经在char_set
中出现。- 如果
current_char
已经在char_set
中,说明当前子串中有重复字符,需要将窗口的起始位置start
向右移动,移除集合中的字符,直到重复字符被移除,以确保当前子串中没有重复字符。- 然后,将
current_char
添加到char_set
中,表示它已经在当前子串中。- 计算当前子串的长度
current_length
,即end - start + 1
,并将其与max_length
进行比较,更新max_length
的值,以保持最大子串长度的记录。最终,函数返回
max_length
,表示字符串中不含有重复字符的最长子串的长度。
时间复杂度为 O(n),其中 n 是字符串的长度,因为它只需要遍历一次字符串。同时,它使用了集合来存储字符,以快速判断重复字符,因此也具有较低的空间复杂度。
2) C
#include <stdio.h>
#include <string.h>
int lengthOfLongestSubstring(char* s) {
int max_length = 0; // 最长子串的长度
int start = 0; // 滑动窗口的起始位置
int char_set[256]; // 集合,用于存储当前子串中的字符是否出现
memset(char_set, 0, sizeof(char_set)); // 初始化为0,表示字符尚未出现
for (int end = 0; s[end] != '\0'; end++) {
char current_char = s[end];
// 如果字符已经在当前子串中出现,将窗口起始位置移动到重复字符的下一个位置
while (char_set[current_char]) {
char_set[s[start]] = 0; // 将起始位置的字符从集合中移除
start++;
}
// 计算当前子串的长度并更新最大子串长度
int current_length = end - start + 1;
max_length = (current_length > max_length) ? current_length : max_length;
// 将当前字符标记为已出现
char_set[current_char] = 1;
}
return max_length;
}
int main() {
char s[] = "abcabcbb";
int result = lengthOfLongestSubstring(s);
printf("Length of longest substring: %d\n", result); // 输出:3
return 0;
}
3.2 哈希表法
def lengthOfLongestSubstring(s: str) -> int:
max_length = 0 # 最长子串的长度
start = 0 # 滑动窗口的起始位置
char_index = {} # 字符索引,用于存储字符的最后出现位置
for end, current_char in enumerate(s):
# 如果字符已经在当前子串中出现,将窗口起始位置移动到重复字符的下一个位置
if current_char in char_index and char_index[current_char] >= start:
start = char_index[current_char] + 1
# 计算当前子串的长度并更新最大子串长度
current_length = end - start + 1
max_length = max(max_length, current_length)
# 记录字符的出现位置
char_index[current_char] = end
return max_length
写在最后:
代码为leetcode官方提供
本文只为个人学习记录★,°:.☆( ̄▽ ̄)/$:.°★ 。