3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
提示: 0 <= s.length <= 5 * 104 s 由英文字母、数字、符号和空格组成
解题:滑动窗口(python)
这道题作为leetcode题库里的第三题,相信很多同学都做过了,总得来说还是蛮简单的
正常有思路的话肯定第一反应就是想到滑动窗口,O(n)时间复杂度解决问题,没啥好说的
整个玄乎一点的 :
如果套个模版从普遍性的角度去解题
首先考虑最暴力的解法,把每一个字符作为起始字符,然后依次便利其后的字符,判断是否满足条件(对于一个给定的字符串来说,我们已知它的长度,并且每个起始字符独立无关,所以最外层是一个for循环。双指针通常由两个循环组成,一般情况下,如果前面两个条件有一个不满足,我们会选择while循环)
这道题里判断条件很简单,它的要求是不含有重复字符
,所以去重是这道题的关键,提示给到了字符由英文字母、数字、符号和空格
组成,考虑到的编码未知的情况下,就不用数组来计数了,正常的哈希集就行,python里的数据结构是集合。
相对于子列,子串一定是连续的,所以我们可以进一步划分子问题(把每个独立的字符取出作为起点是划分子问题的第一步),那么对于一个子串,为了保证它其中没有重复的字符,子串的子串也不能有重复字符,所以该条件可以用作剪枝(这其实是一个适用于dp的蛮经典的条件:在递归里,它是重复调用的基础;在递推里,它是简化运算的基础,不过这道题过于简单,题干条件直接当剪植用了)
最后的最后用个滑动窗口解决一下重复子问题(一开始,在整个解决问题的算法的架构上,每一个起始字符是独立的,它们是“同时”向后扩展,然后一步步判断是否满足条件,然后结束进程【就是所谓的剪植】,但是为了解决重复子问题,我们不能只对子串末尾进行剪植,而应该两头同时“剪”,这就是滑动窗口左指针移动的原因,也是这道题的另一个视角)
代码:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
sets = set()
n = len(s)
r, ans = -1, 0
for l in range(n):
if l != 0:
sets.remove(s[i - 1])
while r + 1 < n and s[r + 1] not in sets:
sets.add(s[r + 1])
r += 1
ans = max(ans, rk - i + 1)
return ans
代码详解:
这个代码cv力扣官方的,是人家精简之后的产物了
第一步:定义变量
首先前两个
1.定义哈希集合sets = set()
2.获取字符串长度.n = len(s)
没啥好说的
然后是
3.答案ans = 0
用于计数
4.左指针left
因为独立所以直接定义在外循环里
最经典的是右指针的定义r = -1
抛开python本身的索引特性,它这里定义的-1指的就是在第一个字符的左边,同时也是在最左指针的左边,这个不可能出现在运算过程中的状态说明了
运算过程中,左右指针是可以重叠的(虽然这道题中仅开始时存在)
右指针是运算的核心
确保了左右指针包括在子串内的同时,保证了边界条件不会出现混乱
这模版写的多的还好,没啥亮点,自己写的话思路就不简单有这么清晰了,总结一下算是给自己偷个懒(反正我自己经常反应不过来)
最后就是正常的循环嵌套,没啥好说的