题目
我们首先来看看这个题目,我了解到这道题目是在一个同学的面经上,刚看到这道题,想着完了,我碰到这道题必死。但是当我真正看懂了这个题的时候其实觉得还好,不算超级难。ps:那位小伙伴面的是字节前端呦,有兴趣的话可以去看看他的面经👉
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。解答1:滑动窗口
说实话我之前压根没有用过滑动窗口写过什么题,一般都是双指针啥的就基本可以解决吧,这个还是要归罪于A的题少吧,手动捂脸~
我们来认识一下这个奇怪的算法。
滑动窗口是为了解决字符串/数组的子元素问题,能快速找到字符串或者是数组的符合要求的子串,避免了嵌套循环带来的超时问题。
滑动窗口实际上就是维护两个指针形成的一个内部窗口,如果符合要求,前面的指针前移,窗口扩大;不符合要求,后面的指针前移,缩小窗口。
我们现在来看看这个该怎么解决这道题。。
先来分析一下需求:
1. 我们要找的是不含有重复元素的最大子字符串,
2. 如果给我们的字符串长度是1或0那么我们就返回其长度即可;
3. 我们可以设置窗口的头和尾是0和1,那么这个窗口的最大长度就是1;
4. 在我们设置的尾指针还未溢出数组时,我们就都要进行判断;’
5. 如果这个尾指针所指的元素在我们的窗口中出现过的话,我们就需要缩小窗口大小,移动头指针到重复元素的下一位;
6. 如果没有出现过,我们需要扩大窗口的大小,将尾指针后移,而且将窗口的长度增加。/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 动态窗口解法
var len=s.length;
if(len<=1) return len;
var index=-1, max=1,head=0,tail=1;
while(tail<len){
index=s.substring(head,tail).indexOf(s[tail]);
if(index!=-1&& head <tail){
head+= index+1;
tail++;
}else{
tail++;
if(tail-head>max){
max=tail-head;
}
}
}
return max;
};
这种方法虽然比使用堆栈之类的暴力破解方法好,但是我觉得每一次循环,都要分割数组,获取位置有点麻烦哎,那么我们能不能使用一种简单的方法来改进一下这个办法呢;
滑动数组(哈希map改进)
我们使用了哈希map来存储每个字符的使用情况,如果访问过这个字符我们将设置其为true,未访问过则为false,这个访问过指的是,我们将其放入了我们的滑动窗口中。
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 动态窗口哈希表解法
let len=s.length;
if(len<=1) return len;
//设置一个哈希表,char=>boolean表示元素是否存在在窗口中
let map={};
let max=0,i=0,j=0;
while(i<len && j<len){
if(!map[s[j]]){
max=Math.max(j-i+1,max);
map[s[j]]=true;
++j;
}else{
map[s[i]]=false;
++i;
}
}
return max;
};
我以为我使用了哈希表的方法,已经满足了,偷偷笑。
然而当我点开题解,看到一位大佬正好和我的思路差不多的时候,还发现他对它进行了优化啊啊啊,这就是大佬和菜鸡之间的差距吗?
我看了看那,确实可以在判断是否重复的地方进行优化,学无止境啊。
让我们一起来看看大佬的优化思路。
我们在上面的解法中,如果出现重复的元素的时候,我们将对头元素进行向前移动一位,进行循环,其实完全没必要一位一位地移动,这样会造成很大的资源浪费。
所以我们在发现重复元素的时候,只需要替换头指针的值即可。/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 动态窗口哈希表解法
let len=s.length;
if(len<=1) return len;
//设置一个哈希表,char=>boolean表示元素是否存在在窗口中
let map=new Map();
let max=0,i=0,j=0;
while(i<len && j<len){
if(map.has(s[j]) && map.get(s[j])>i){
i=map.get(s[j])+1;
}
max=Math.max(j-i+1,max);
map.set(s[j],j);
j++;
}
return max;
};
这道题,暂时就先用这个方法解决吧(其他的我也不会……),等我学会了再来更新哈哈哈。