滑动窗口算法——剑指offer 48.最长不含重复字符的子字符串

题目

我们首先来看看这个题目,我了解到这道题目是在一个同学的面经上,刚看到这道题,想着完了,我碰到这道题必死。但是当我真正看懂了这个题的时候其实觉得还好,不算超级难。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;
};

我以为我使用了哈希表的方法,已经满足了,偷偷笑。滑动窗口算法——剑指offer 48.最长不含重复字符的子字符串_第1张图片
然而当我点开题解,看到一位大佬正好和我的思路差不多的时候,还发现他对它进行了优化啊啊啊,这就是大佬和菜鸡之间的差距吗?

我看了看那,确实可以在判断是否重复的地方进行优化,学无止境啊。
让我们一起来看看大佬的优化思路。

我们在上面的解法中,如果出现重复的元素的时候,我们将对头元素进行向前移动一位,进行循环,其实完全没必要一位一位地移动,这样会造成很大的资源浪费。
所以我们在发现重复元素的时候,只需要替换头指针的值即可。
/**
 * @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;
};

这道题,暂时就先用这个方法解决吧(其他的我也不会……),等我学会了再来更新哈哈哈。

你可能感兴趣的:(算法学习)