携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解法思路——暴力枚举
首先关注一下示例3,pwke 不算一个子串,而是一个序列。也就是说,子串是连续的,子序列是子元素组成的串。这就是这题的注意点了,千万不要写成了子序列然后 debug 半天。
那么既然是要找最长的子串,最简单的,对于字符串 s,用两层循环。我们可以用外层循环对字符串从每个下标 i 为起点,遍历从下标 i + 1 到 s.length - 1 的字符串,一旦碰到重复的,就结束循环,统计当前的 不重复子串长度。
题解
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const lens = s.length;
let maxLens = 0;
for(let i=0; i<lens; ++i) {
let temp = s[i], cnt = 1;
for(let j=i+1; j<lens; ++j) {
if(temp.includes(s[j])) {
break;
}
cnt++;
temp+=s[j];
}
maxLens = Math.max(maxLens, cnt);
}
return maxLens;
};
解题思路————滑动窗口+Set
暴力枚举需要对每个位置上的字符一直枚举到结尾,会有重复枚举的问题,极大的影响效率,那么有没有什么办法可以减少对字符串的遍历呢?
这里我们就需要使用 滑动窗口+Set 了。这里我使用两个变量 i 和 j 来模拟滑动窗口的左右区间,同时为了方便查找区间的元素,所以使用 Set 来判断当前遍历到的字符是否存在于滑动窗口中。
- 首先滑动窗口不断向右移动。
- 判断当前遍历到的字符
w是否存在于Set集合中。 - 如果不存在当前字符
w,那么就将当前元素添加到Set集合中(添加元素到滑窗中),并且将更新maxLens变量maxLens = Math.max(maxLens. Set.size)。 - 如果存在当前字符
w,则我们要将Set集合中 w 字符 及其之前 的所有字符都 删除,然后当前字符重新添加到Set集合中(这个步骤有点表述不清,拿字符串pwwkerw来说,我们到第一个w为止,滑窗的size = 2,所有字符为pw,那么遍历到第二个w的时候,我们为了让滑窗中的 子串 没有重复字符,那么pww这个字符串,我们需要删除前两个字符,才能保证 子串 是不含重复字符的)。 - 重复 2、3、4 步骤,最后输出
maxLens。
题解
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const lens = s.length, wordSet = new Set();
if(lens === 0) return 0;
else if(lens === 1) return 1;
let maxLens = 0;
let i=0, j=0;
while(i < lens) {
if(!wordSet.has(s[i])) {
wordSet.add(s[i]);
maxLens = Math.max(wordSet.size, maxLens);
} else {
while(wordSet.has(s[i])) {
wordSet.delete(s[j]);
j++;
}
wordSet.add(s[i]);
}
i++;
}
return maxLens;
};