题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
错误解法
一上来没看懂题意,闹了乌龙,以为是区不重复字符的个数💣💣💣
//错误解法!!!不含有重复字符的最长子串的长度,而不是不重复的字符串
var lengthOfLongestSubstring = function(s) {
s = s.split('');
let newS = [];
for(let i=0;i<s.length;i++){
if(!newS.includes(s[i])){
newS.push(s[i])
}
}
return newS.length;
};
解答
题解1: 暴力求解
思路
很容易想到,我们可以将所有字串都列举出来,然后获得最长字串的长度,就是我们的答案。
- 将字符串s转为数组(arr),声明len为数组长度。
- 如果s长度为0或1,直接返回len。
- 外层循环定义str存放子串内容,默认存放了当前元素。内部循环判断str是否存在下一个元素。如果存在,更新最大长度值,跳出本次循环。如果不存在放入str中,然后更新最大长度。
- 返回最大子串长度。
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const arr = Array.from(s);
const len = arr.length;
if(len <2) return len;
let maxLen = 0;
for(let i=0;i<=len;i++){//这里执行了n次
let str = [arr[i]];
for(let j=i+1;j<len;j++){//这里执行了n*n次
if(str.includes(arr[j])){
maxLen = Math.max(str.length,maxLen)
break
}
str.push(arr[j]);
maxLen = Math.max(str.length,maxLen)
}
}
return maxLen;
};
贴一下运行打印还是比较清晰的,每次执行将所有可能的无重复子串列出,长度最长的子串即为我们最终结果。
| 提交结果 | 执行用时 | 内存消耗 | 语言 |
|---|---|---|---|
| 通过 | 452 ms | 47.6 MB | JavaScript |
双重for循环逐一枚举无疑是特别消耗时间的。
题解2: 滑动窗
思路1
滑动窗就是利用队列的思想,将队列的元素与模板元素进行比较,如果队列中没有目标元素,就将目标元素填充到队列中。如果有目标元素,就不继续滑动窗口,而是将队列元素返回。
- 创建一个Set对象用于存放无重复字符的子串,Set是唯一值的集合,所有此处更加贴合。声明max存放最大子串长度。
- 如果s长度为0或1,直接返回len。
- 循环字符串s,如果set中没有就新增,并更新max;如果有,也就是不符合子串的要求了,那么将set以前存入的重复的元素删除,并将当前的元素新增到set。
- 返回最大长度max。
var lengthOfLongestSubstring = function(s) {
let set= new Set();
let max = 0;
if(s.length < 2){return s.length;}
let j=0;
for(let i=0;i<s.length;i++){
if(!set.has(s[i])){
set.add(s[i]);
max = Math.max(set.size,max)
}else{
while(set.has(s[i])){
set.delete(s[j]);
j++;
}
set.add(s[i]);
}
}
return max;
};
结合上图,我们很清晰的可以看到,我们实现的是左面的索引逐步收缩,而右侧的索引逐步扩张,将可能的出现在子串中的元素逐步包含进来,每次循环与暂存的最长子串max比较,最终获得到最长子串的长度。
| 提交结果 | 执行用时 | 内存消耗 | 语言 |
|---|---|---|---|
| 通过 | 84 ms | 46 MB | JavaScript |
思路2
str.indexOf(searchValue, fromIndex)从fromIndex位置开始查找元素,如果s字符串中从formIndex位置起查找遍历的元素s[i],查找到的索引小于当前遍历索引i,说明有重复元素;
每次循环中的i相当于子串的终止位置,终止位置-起止位置+1就是新子串的长度。
- 声明minIndex为最长子串起始位置,默认为0;声明最长子串长度len默认为0;
- 遍历s字符串,如果s字符串从minIndex子串起止位置开始查找,如果前面有与之重复的元素,则更新minIndex为当前查找到位置的下一位。
- 如果没有重复,则更新len子串长度(新子串长度为终止位置-起始位置+1与len原本值中取较大数)。
var lengthOfLongestSubstring = function(s) {
let minIndex = 0;
let len =0;
for(let i=0;i<s.length;i++){
s.indexOf(s[i],minIndex) < i?
minIndex = s.indexOf(s[i],minIndex)+1:
len = Math.max(len,i-minIndex+1)
}
return len
}
| 提交结果 | 执行用时 | 内存消耗 | 语言 |
|---|---|---|---|
| 通过 | 80 ms | 43.7 MB | JavaScript |