这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战
题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例1
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
思路
题目要寻找的是一个字符串的最长子串,很简单的就会想到,穷举这个字符串所有符合要求的子串,找出最长的那一个,即为题目所求
子串问题
这里考虑一个问题,假如从字符串的第一个位置开始找子串,记子串开始位置为 `i`
假如在后面的位置 `k + 1` 发现重复元素,意味着从 `i` 到 `k` 的子串是无重复的,长度为 `k - i + 1`;
那么在寻找下一个子串的时候,开始位置为 `i + 1`,从 `i + 1` 到 `k` 位置的子串一定也是符合要求无重复的子串
可以从 `k + 1` 开始判断下面的子串是不是符合要求,而不需要从 `i + 2` 开始
重复问题
这里可以使用 `Set`,因为可以很好的添加、删除以及判断元素是否存在
边界问题
输入的字符传长度为空,需要直接返回 0
代码如下
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
if(s === '') return 0;
const strSet = new Set();
const n = s.length;
let ans = 0, right = -1;
for(let i = 0; i < n; i++) {
// 将窗口前部分后移
if(i != 0) {
strSet.delete(s.charAt(i - 1));
}
while(right + 1 < n && !strSet.has(s.charAt(right + 1))) {
strSet.add(s.charAt(right + 1));
// 将窗口后部分后移
right++;
}
// 窗口滑动结束,保留我们需要的值
ans = Math.max(ans, right - i + 1);
}
return ans;
}
小结
上述的解法可以归纳为 滑动窗口,解法在不经意间,其实定义了两个指针 i 和 right,每次循环都是判断从 i 到 right 位置上的子串是不是符合要求,符合则将 right + 1,即把窗口向右移动,再次判断以 i 为起始位置的子串是不是符合要求;若移动过程中,不满足要求或者窗口滑到最后,则需要将窗口的前部分向右移动,再次判断以 i + 1 为起始位置的子串是不是符合要求;在滑动窗口的过程中,保留或者计算题目需要的值,等到整个窗口都滑动完成,保留的值即为题目所求
LeetCode 👉 HOT 100 👉 无重复字符的最长子串 - 中等题 ✅