题目
给定一个字符串 s
,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc"
,所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b"
,所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke"
,所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke"
是一个子序列,不是子串。
滑动窗口技术
思路:
这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
时间复杂度:O(n)
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:if not s:return 0left = 0lookup = set()n = len(s)max_len = 0cur_len = 0for i in range(n):cur_len += 1while s[i] in lookup:lookup.remove(s[left])left += 1cur_len -= 1if cur_len > max_len:max_len = cur_lenlookup.add(s[i])return max_len
这段代码使用滑动窗口技术来找出字符串中最长的无重复字符子串的长度。以下是逐行解释:
-
if not s: return 0
检查输入字符串是否为空,如果为空则直接返回0。 -
left = 0
初始化左指针left
为0,表示当前窗口的起始位置。 -
lookup = set()
创建一个空集合lookup
,用于记录当前窗口中的字符。 -
n = len(s)
计算字符串s
的长度,并赋值给变量n
。 -
max_len = 0
初始化最长子串长度max_len
为0。 -
cur_ len = 0
初始化当前窗口长度cur_ len
为0。 -
for i in range(n):
遍历字符串中的每一个字符,索引为i
。 -
cur_ len +=1
进入循环后,先将当前窗口长度增加1,假设当前字符可以加入窗口。 -
while s[i] in lookup:
检查当前字符s[i]
是否已经在当前窗口中存在。如果存在,则需要调整窗口左边界:a. lookup.remove(s[left])
移除集合中的左指针指向的字符,表示该字符不再属于当前窗口。b. left +=1
将左指针向右移动一位,缩小窗口范围。c. cur_ len -=1
由于移除了一个字符,当前窗口长度减1。 -
if cur_ len > max_len: max_len = cur_ len
检查当前窗口长度是否为最大值,并更新max_len
。 -
lookup.add(s[i])
将当前字符s[i]
添加到集合中,表示该字符已加入当前窗口。 -
return max_len
遍历结束后,返回最长无重复子串的长度max_len
。
#include<string.h> //首先额外引入一个新的字符串库
int lengthOfLongestSubstring(char* s) {int len=strlen(s); //得到字符串的长度int i = 0; //i是循环变量,从第一个字符开始找最长的无重复字母的子串int result = 0; //result 为最后的返回的结果,求长度最长时,result至为0;求长度最短时,result至为INT32_MAXint count = 0; //count 为每次循环的最长的无重复字母的子串的长度int b; //b 为递归调用循环变量while (s[i]){int flag = 1; //flag 为标志变量,首先至为1int j; //里层循环的变量jfor (j = 0; j < i; j++){if (s[i] == s[j])//如果找到重复的的字母,那么就{flag = 0; //标志变量至为0b = j; //递归变量b赋值为j,即在子串的与s[i]重复的第一个字母break; //跳出里层循环}}if (flag == 1)//如果遍历了一遍,如果没发现重复的字母,flag还是等于1{i++; count++; //子串的长度加1result = result > count ? result : count; //更新result的结果:count和原来result中最大的数}else{break; //否则,退出外层循环}b = i; //如果i=1时,s[i](即s[b],即s[1])和s[1]相同时,递归时直接往后移一位就行;;当i>1,也是从子串的与s[i]重复的第一个字母s[b]下一个字母————s[b+1]开始,}// 设置递归出口条件if (b+1<len) //注意不建议*(s+b+1),这样算是空指针解引用,在力扣上报错了,但在VS上没报错 //所以必须保证b+1<len{count = lengthOfLongestSubstring(s+b+1);result = result > count ? result : count;//必须要有这一步,否则也会出错//将递归的结果与上级函数的result比较}return result;
}