今日打卡,LeetCode第三题(源码详解分享)
Leetcode
题目:3. 无重复字符的最长子串
可以先看官方的解释,枚举所有起始下标的可能,以abcabcbb
为例:
- 以 (a)bcabcbb 开始的最长字符串为 (abc)abcbb;
- 以 a(b)cabcbb 开始的最长字符串为 a(bca)bcbb;
- 以 ab©abcbb 开始的最长字符串为 ab(cab)cbb;
- 以 abc(a)bcbb 开始的最长字符串为 abc(abc)bb;
- 以 abca(b)cbb 开始的最长字符串为 abca(bc)bb;
- 以 abcab©bb 开始的最长字符串为 abcab(cb)b;
- 以 abcabc(b)b 开始的最长字符串为 abcabc(b)b;
- 以 abcabcb(b) 开始的最长字符串为 abcabcb(b)。
官方源码:
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:# 哈希集合,记录每个字符是否出现过occ = set()n = len(s)# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动rk, ans = -1, 0for i in range(n):if i != 0:# 左指针向右移动一格,移除一个字符occ.remove(s[i - 1])while rk + 1 < n and s[rk + 1] not in occ:# 不断地移动右指针occ.add(s[rk + 1])rk += 1# 第 i 到 rk 个字符是一个极长的无重复字符子串ans = max(ans, rk - i + 1)return ans
源码解释
occ = set()
,是初始化了一个集合occ,用来存储当前窗口中的字符
n = len(s)
,n是字符串的长度
rk, ans = -1, 0
,rk初始化为-1,ans初始化为0,可以理解为简单的初始化赋值
for i in range(n):
,遍历每个字符,这里遍历的是下标,range()中的n是整型,范围长度是n,是从0开始,如果是指定起始值,例如起始值为1,range(1,n),n是闭环,range的输出不包括n,可以简单的实现下:for i in range(1,8):print(i) # i的结果是1、2、3、4、5、6、7
for循环里中的流程
,
ans = max(ans, rk - i + 1)
,比较ans
和rk - i + 1
的值,谁最大就赋值给ans
- 当
i=0
的时候,if i != 0:
下的语句就不运行了,可以直接先不用看,观察下while rk + 1 < n and s[rk + 1] not in occ:
,这里就有一个注意的点,rk
的值,rk
的值是初始化为-1
,相当于是将s
字符串的左边的边界初始化为-1
,但是正序下标的没有是起始下标为-1
的说法(除非是倒序),所以当i=0
时,rk + 1 < n
合理成为了s
的边界,且s[rk + 1] not in occ
这个字符串第一个元素不在集合中,while
语句就成立了,occ.add(s[rk + 1])
,将字符串添加至集合中,后面还有一个rk += 1
,意思就是while
循环遍历,所以i=0
时,连续不重复的字符是abc
。- 当
i!=0
的时候,就需要看下if i != 0:
的时候,注意:这时rk=3
,ans=3
,occ.remove(s[i - 1])
移除s[0]
,就是移除了a这个元素,那么此时,occ
里面只有b和c
,两个元素,再执行while rk + 1 < n and s[rk + 1] not in occ:
,第一个是成立的,第二个是不成立的,因为s[rk+1]=s[4]='b'
,这个b
已经在集合occ
中,所以可以不用看下面的循环,此时连续不重复的字符长度还是3
步骤说明
- 初始化变量:
occ
:集合,存储当前窗口中的唯一字符。n
:字符串长度。rk
:右指针,初始为-1(表示尚未开始移动)。ans
:记录最长子串长度,初始为0。
- 遍历字符串(左指针移动):
- 左指针
i
从0开始遍历每个字符。 - 每次循环,若
i
不为0,则从集合中移除前一个字符s[i-1]
(左指针右移,缩小窗口左边界)。
- 左指针
- 扩展右指针:
- 在左指针固定的情况下,尽可能向右移动右指针
rk
,直到遇到重复字符。 - 将新字符加入集合
occ
,并更新rk
。
- 在左指针固定的情况下,尽可能向右移动右指针
- 更新最大长度:
- 每次右指针停止后,计算当前窗口长度
rk - i + 1
,并与ans
比较取最大值。
- 每次右指针停止后,计算当前窗口长度