代码思路
1. 滑动窗口法
使用 滑动窗口法,通过维护一个窗口(由 start_index 和 end 定义),动态调整窗口的大小,确保窗口内的字符没有重复。
2. 哈希表记录字符位置
-
使用一个数组
hash_map[128]来记录每个字符最后一次出现的位置。-
数组大小为
128,因为 ASCII 字符的范围是0到127。 -
hash_map[c]表示字符c最后一次出现的位置。
-
3. 滑动窗口的维护
-
start_index表示当前窗口的起始位置。 -
end表示当前窗口的结束位置。 -
如果当前字符
s[end]已经出现过,并且它的上一次出现位置在start_index之后,则更新start_index到hash_map[s[end]],即跳过重复字符。
4. 更新最大长度
-
每次更新窗口后,计算当前窗口的长度
end - start_index + 1,并更新max_len。
int lengthOfLongestSubstring(char* s)
{
int hash_map[128]={0}; // 用哈希表记录每个元素最后一次出现的位置,128是为了覆盖所有ASCII
int max_len=0;
int start_index=0;
for(int end=0; s[end]; end++) // s[]是字符串的第n个字符,当end='\0'时结束循环
{
// s[end]传入字符,例如:字符串‘abcabcbb’
// end=0,对于字符‘a’, s[end]='a',hash_map['a']=hash_map=[97]=0
// 更新hash_map['a']=end+1=1,表示‘a’最后一次出现在索引0
// 检查当前字符 s[end] 是否在当前窗口内重复出现。
// 如果是,则更新窗口的起始位置 start_index。
// 如果 map[s[end]] > start_index,说明字符 s[end] 在当前窗口内已经出现过。
// 换句话说,字符 s[end] 上一次出现的位置在当前窗口的范围内。
if(hash_map[s[end]]>start_index)
{
start_index=hash_map[s[end]];
}
hash_map[s[end]]=end+1;
// end-start_index+1:当前滑动窗口的长度
max_len=fmax(max_len, end-start_index+1);
}
return max_len;
}
这道题很经典,用这道题来好好捋顺一下滑动窗口
关键点
-
记录字符map[s[end]]的作用:s[end]最后一次出现的位置。 -
判断字符map[s[end]] > start_index的作用:s[end]是否在当前窗口内重复出现。 -
更新
跳过重复字符,确保窗口内的字符没有重复。start_index的作用:
if (map[s[end]] > start_index) 的作用是检查当前字符是否在当前窗口内重复出现。如果是,则更新窗口的起始位置 start_index,确保窗口内的字符没有重复。这是滑动窗口算法的核心逻辑之一。
下面形象描述一过程
给定一个字符串 s = "abcabcbb",找到其中最长的无重复字符的子串。
滑动窗口的过程
我们用两个指针 start_index 和 end 来表示滑动窗口的起始和结束位置。初始时,start_index = 0,end 从 0 开始向右移动。
初始状态
字符串: a b c a b c b b
索引: 0 1 2 3 4 5 6 7
遍历过程
1. end = 0,字符 'a'
-
窗口:
[a] -
窗口长度:
0 - 0 + 1 = 1 -
max_len = 1 -
map['a'] = 1
窗口: [a] b c a b c b b
索引: 0
2. end = 1,字符 'b'
-
窗口:
[a, b] -
窗口长度:
1 - 0 + 1 = 2 -
max_len = 2 -
map['b'] = 2
窗口: [a b] c a b c b b
索引: 0 1
3. end = 2,字符 'c'
-
窗口:
[a, b, c] -
窗口长度:
2 - 0 + 1 = 3 -
max_len = 3 -
map['c'] = 3
窗口: [a b c] a b c b b
索引: 0 1 2
4. end = 3,字符 'a'
-
字符
'a'已经出现过,上一次出现的位置是0。 -
更新
start_index = map['a'] = 1。 -
窗口:
[b, c, a] -
窗口长度:
3 - 1 + 1 = 3 -
max_len = 3 -
map['a'] = 4
窗口: a [b c a] b c b b
索引: 1 2 3
5. end = 4,字符 'b'
-
字符
'b'已经出现过,上一次出现的位置是1。 -
更新
start_index = map['b'] = 2。 -
窗口:
[c, a, b] -
窗口长度:
4 - 2 + 1 = 3 -
max_len = 3 -
map['b'] = 5
窗口: a b [c a b] c b b
索引: 2 3 4
6. end = 5,字符 'c'
-
字符
'c'已经出现过,上一次出现的位置是2。 -
更新
start_index = map['c'] = 3。 -
窗口:
[a, b, c] -
窗口长度:
5 - 3 + 1 = 3 -
max_len = 3 -
map['c'] = 6
窗口: a b c [a b c] b b
索引: 3 4 5
7. end = 6,字符 'b'
-
字符
'b'已经出现过,上一次出现的位置是5。 -
更新
start_index = map['b'] = 6。 -
窗口:
[c, b] -
窗口长度:
6 - 6 + 1 = 1 -
max_len = 3 -
map['b'] = 7
窗口: a b c a b c [b] b
索引: 6
8. end = 7,字符 'b'
-
字符
'b'已经出现过,上一次出现的位置是7。 -
更新
start_index = map['b'] = 8。 -
窗口:
[b] -
窗口长度:
7 - 7 + 1 = 1 -
max_len = 3 -
map['b'] = 8
窗口: a b c a b c b [b]
索引: 7
最终结果
-
最大无重复字符子串的长度是
3。 -
对应的子串是
"abc"(从索引0到2)或"bca"(从索引1到3)等。
通过上述过程,可以看到:
-
窗口的扩展:
end指针向右移动,扩展窗口。 -
窗口的收缩:
当遇到重复字符时,start_index指针向右移动,收缩窗口。 -
窗口长度的计算:
每次更新窗口后,计算当前窗口的长度end - start_index + 1,并更新max_len。
总结
滑动窗口的核心思想是通过两个指针动态维护一个窗口,确保窗口内的字符没有重复,并记录最大长度。
下面是模板
int slidingWindow(char* s)
{
int map[128] = {0}; // 哈希表,记录字符的状态
int start = 0; // 窗口的起始位置
int max_len = 0; // 记录最大长度
for (int end = 0; s[end]; end++) // 遍历字符串
{
// 更新窗口的状态(例如:记录字符的出现次数或位置)
map[s[end]]++;
// 如果窗口不满足条件,则收缩窗口
while (/* 窗口不满足条件 */)
{
map[s[start]]--; // 更新窗口的状态
start++; // 收缩窗口
}
// 更新结果(例如:计算窗口的长度)
max_len = fmax(max_len, end - start + 1);
}
return max_len; // 返回结果
}
C语言&spm=1001.2101.3001.5002&articleId=146424914&d=1&t=3&u=27e5e140f29f490e903decb27e40f94d)
1920

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



