【LeetCode刷题】NO.7

125 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一.题目

567. 字符串的排列 给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 ****的排列。如果是,返回 true ;否则,返回 false 。 换句话说,s1 的排列之一是 s2 的 子串 。

示例 1:

输入: s1 = "ab" s2 = "eidbaooo"
输出: true
解释: s2 包含 s1 的排列之一 ("ba").

示例 2:

输入: s1= "ab" s2 = "eidboaoo"
输出: false

提示:

  • 1 <= s1.length, s2.length <= 104
  • s1 和 s2 仅包含小写字母

二、思路分析:

  • 看到子串问题首先想到滑动窗口的思路:即通过左右指针不断地移动形成字符窗口与目标串进行比对,首先将目标串的所有字符全部变成targets对象的键,字符的数量为该键的值,通过右移右指针的循环不断地将右指针指向的字符加入滑动窗口window对象中,然后将window对象中该字符的数量与targets对象该字符比对,如果相等则valid自增。
  • 如果valid值等于targets对象键数的时候就可以进行缩小滑动窗口的操作
  • 注意这道题不同于最小覆盖子串问题,不需要找到最小的覆盖子串,只需要比较找到的满足要求的子串是否等于目标串的长度即可,因为设计到s1的排列所以只需要比较长度

三、代码:

/**
 * @param {string} s1
 * @param {string} s2
 * @return {boolean}
 */
var checkInclusion = function(s1, s2) {
    //利用滑动窗口的思想求解问题
    //同样利用左右指针
    let left = right = 0
    let targets = {}
    for(const target of s1){
        targets[target] = (targets[target] || 0)+1
    }
    let window = {}
    let valid = 0
    while(right < s2.length){
        //移入右指针指定的字符并将右指针在右移
        let c = s2[right]
        right++
        
        //窗口更新操作
        if(targets[c]){
            window[c] = (window[c] || 0) + 1
            if(targets[c] == window[c]){
                valid++
            }
        }
        while(valid == Object.keys(targets).length){
            if(right - left == s1.length) return true
            let d = s2[left]
            left++
            if(targets[d]){
                if(targets[d] == window[d]) valid--
                window[d]--
            }
        }
    }
    return false
};

四、总结:

通常这类字符串问题我们都可以利用滑动窗口的思想进行求解,如果能够熟练掌握滑动窗口的思想通常对于这种字符串问题利用一个滑动窗口框架就能够全部求解出来。这个代码提交出来的运行时间比别个的要长一些,所以看到了这道题其他人的题解发现还是有很多补充的点,针对特定情况我们可以对框架进行优化,看到别人的题解中也是利用了滑动窗口的思想,但是在滑动窗口的长度未达到targets的长度时会跳过直到大于targets的长度然后进行一步步的比对,这种优化也很值得我学习0.0