这是leetcode面试刷题一题多解系列的第5篇,找出给定字符串不含有重复字符的 最长子串 的长度,练习下滑动窗口解决思路。
题目
今天跟大家一起看一道找出不重复字符的 最长子串 的问题,了解滑动窗口(也叫双指针)的思路。
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。
来源:力扣(LeetCode) 链接:leetcode.cn/problems/lo… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解1---滑动窗口 (Set 实现)
滑动窗口是一种常用的解决字符串问题的方法,可以有效地处理无重复字符的最长子串问题。具体步骤如下:
- 创建两个指针,一个指向子串的起始位置(
left
),一个指向子串的结束位置(right
)。
let left = 0;
let right = 0;
- 创建一个 Set 数据结构用于存储当前窗口内的字符,用于判断字符是否重复。
const charSet = new Set();
- 创建一个变量
maxLen
用于记录最长子串的长度,初始化为 0。
let maxLen = 0;
- 循环遍历字符串,将字符逐个加入窗口,并判断是否重复。
- 如果字符不重复,则将其加入窗口,并更新
right
指针。 - 如果字符重复,则从窗口中移除左边界的字符,并更新
left
指针。
同时,每次更新窗口时,都更新一次 maxLen
的值,记录当前最长子串的长度。
while (right < s.length) {
if (!charSet.has(s[right])) {
charSet.add(s[right]);
right++;
maxLen = Math.max(maxLen, right - left);
} else {
charSet.delete(s[left]);
left++;
}
}
- 循环结束后,
maxLen
就是最长无重复字符的子串的长度。
// 完整代码
var lengthOfLongestSubstring = function (s) {
let left = 0;
let right = 0;
const charSet = new Set();
let maxLen = 0;
while (right < s.length) {
if (!charSet.has(s[right])) {
charSet.add(s[right]);
right++;
maxLen = Math.max(maxLen, right - left);
} else {
charSet.delete(s[left]);
left++;
}
}
return maxLen;
};
这种方法的时间复杂度为 O(n),因为每个字符最多被访问两次(加入窗口和从窗口中移除)。空间复杂度为 O(k),其中 k 为字符集的大小,因为 Set 数据结构需要存储窗口内的字符
题解2---滑动窗口 (Map 实现)
通过 Map 数据结构来记录字符及其索引,从而减少了额外的空间复杂度,并且在字符重复时可以直接跳过一部分字符的比较,避免每次都使用 Set 数据结构来判断字符是否重复。
var lengthOfLongestSubstring = function (s) {
const charMap = new Map();
let left = 0;
let right = 0;
let maxLen = 0;
while (right < s.length) {
if (!charMap.has(s[right]) || charMap.get(s[right]) < left) {
charMap.set(s[right], right);
right++;
maxLen = Math.max(maxLen, right - left);
} else {
left = charMap.get(s[right]) + 1;
charMap.delete(s[right]);
}
}
return maxLen;
};
最容易想到的解决方法如暴力穷举法,通过遍历所有可能的子串并判断其是否包含重复字符来找到最长无重复字符的子串,时间复杂度较高,为 O(n^3)。而采用了滑动窗口法,通过维护一个滑动窗口来找到最长无重复字符的子串,时间复杂度较低,为 O(n)。
在实际应用中,滑动窗口法是解决字符串相关问题的常用方法,尤其是处理子串、子数组、子序列等问题时,具有较高的效率和优越的空间复杂度。而在涉及字符比较的问题中,可以通过 Map 数据结构来记录字符及其相关信息,提高查找效率。