这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
链接:leetcode-cn.com/problems/lo…
题解
-
暴力求解.暴力求解的方法很好理解, 就是每次从当前元素向后遍历, 用一个 map 来保存每个出现的字符, 直到遍历到 map 数组已经保存了的字符, 说明找到了以当前元素为起点的子串. 我们找出每个元素的最长子串, 得到最后的值.
具体代码如下, 时间复杂度 O(n^2)
/**
* @param {string} s
* @return {number}
*/
*/
var lengthOfLongestSubstring = function(s) {
const n = s.length
let ans = 0
for (let i = 0; i < n; i++) {
let map = new Map()
let j = i
while (j < n && !map.has(s[j])) {
map.set(s[j], 1)
j++
}
ans = Math.max(ans, j - i)
}
return ans
};
-
滑动窗口. 在暴力方法中, 我们对于每个元素, 都从头开始寻找最长的子串, 其实, 这中间有重复比较, 因为会重复比较已经比较过的部分, 有点类似于 KMP 算法的意思. 我们可以维护一个窗口, 窗口里的元素一定是不重复. 其实只需要我们在 map 中保存每个字符出现的下标, 当再次出现这个字符时, 只需要让窗口的左边移动到保存好的下标的下一位, 那么就可以保证窗口还是正确的. 当然, 移动之前, 需要将这个字符下标之前的字符 map 标记清除, 保证 map 和窗口里的字符是对应的.
具体代码如下, 时间复杂度 O(n)
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const n = s.length
let map = new Map()
let ans = 0
let l = r = 0
while (r < n) {
// 扩大窗口
while (r < n && !map.has(s[r])) {
map.set(s[r], r)
r++
}
ans = Math.max(ans, r - l)
if (map.has(s[r])) {
now = map.get(s[r]) + 1
// 删除非窗口字符的 map 标记
for (let i = l; i < now; i++) {
map.delete(s[i])
}
// 更新左窗口
l = now
}
}
return ans
};