小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
今天我们来看一道字符串相关的题,这里用JavaScript给出三种解法,其中第三种是我想到的最通俗易懂的解法,我也给出了详细的解析过程,可以看看~
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
【解法一】暴力遍历 + Set
最快想到的就是暴力遍历,以每个字符串为开头遍历一遍 也就是总共要遍历两遍
function lengthOfLongestSubstring(s) {
let len = s.length;
let result = 0;
for (let i = 0; i < len; i++) {
let set = new Set();
let maxLen = 0;
// 从i的位置遍历得到最长子串的长度
let j = i;
while (j < len && !set.has(s[j])) {
set.add(s[j]);
maxLen++;
j++;
}
// 取历史最大值
result = Math.max(result, maxLen);
}
return result;
}
【解法二】滑动窗口
这是官方题解的答案,其实不是特别清晰,详细细节看注释
function lengthOfLongestSubstring(s) {
let len = s.length;
let result = 0;
let set = new Set();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
let j = -1;
for (let i = 0; i < len; i++) {
if (i !== 0) {
// 左指针向右移动一格,移除一个字符
set.delete(s[i - 1]);
}
while (j + 1 < len && !set.has(s[j + 1])) {
// 不断地移动右指针
set.add(s[j + 1]);
j++;
}
// 第 i 到 j 个字符是一个极长的无重复字符子串
result = Math.max(result, j - i + 1);
}
return result;
}
【解法三】双指针 - 滑动(收缩)窗口(这种写法通俗易懂)
其实可以通过观察可以优化,我们制作一个窗口,让窗口中的字符串满足题目要求(无重复) 怎么让他满足要求呢? 那就要滑动窗口了,循环去掉左边第一个元素,直到窗口中元素无重复,此时再扩大窗口
滑动窗口有两个关键点:扩张 + 收缩
-
首先(右指针)扩张到滑动窗口不满足条件的时候暂停,
-
(左指针)开始收缩窗口,让窗口满足条件后再进行扩张(右指针)
function lengthOfLongestSubstring(s) {
let len = s.length;
let result = 0;
let set = new Set();
// 左指针用来收缩窗口
let left = 0;
// 右指针用来扩张窗口
let right = 0;
while (left < len) {
// 如果不重复,就不断扩张窗口,元素添加到set中
while (right < len && !set.has(s[right])) {
set.add(s[right]);
right++;
}
// 到这里说明有元素重复了,先记录子串长度,然后收缩窗口
result = Math.max(result, right - left);
// 收缩窗口
set.delete(s[left]);
left++;
}
return result;
}