数据结构进阶篇,字符串滑动窗口专题

3. 无重复字符的最长子串

  「题目:」

  给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。

  「示例:」

  输入: s = "abcabcbb",输出: 3 。

  「解题思路:」

  这种处理子串的问题,通常使用滑动窗口来解决:

  • 使用双指针技巧,左指针维护滑动窗口的左边界,右指针维护滑动窗口的右边界。

  • 不断移动右指针,利用哈希表判断当前元素是否重复出现,如果当前元素已经出现过,则通过哈希表查出该元素第一次出现的位置,并更新左指针的位置。

  • 在窗口滑动的过程中,不断对比当前窗口的长度,从而获取到不重复子串的最大长度。

  时间复杂度:O(n),空间复杂度:O(n)。

const lengthOfLongestSubstring = function(s) {
    let start = 0;
    let ans = 0;
    const record = new Map();

    for (let end = 0; end < s.length; end++) {
       const char = s[end];
       if (record.has(char) && record.get(char) >= start) {
            start = record.get(char) + 1;
       }

       record.set(char, end);

       ans = Math.max(ans, end - start + 1);
    }

    return ans;
};

567. 字符串的排列

  「题目:」

  给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。

  换句话说,s1 的排列之一是 s2 的 子串 。

  s1 和 s2 仅包含小写字母。

  「示例:」

  输入:s1 = "ab" s2 = "eidbaooo",输出:true。

  「解题思路:」

  本道题也可以采用滑动窗口来解决:

  • 由于滑动窗口的大小是固定的,所以移动右指针的过程中,左指针的位置也是已知的。

  • 在窗口滑动的过程中,需要通过两个哈希表来判断字符串 s1 是否为当前窗口字符串的排列。

  时间复杂度:O(nm),空间复杂度:O(n)。

const isMapEqual = (m1, m2) => {
    if (m1.size !== m2.size) {
        return false;
    }

    for (const [key, val] of m1) {
        const val2 = m2.get(key);

        if (val2 !== val || !m2.has(key)) {
            return false;
        }
    }

    return true;
}

const checkInclusion = function(s1, s2) {
    const len1 = s1.length;
    const len2 = s2.length;
    if (len1 > len2) {
        return false;
    }

    const record1 = new Map();

    for (let i = 0; i < s1.length; i++) {
        if (!record1.has(s1[i])) {
            record1.set(s1[i], 0);
        }

        record1.set(s1[i], record1.get(s1[i]) + 1);
    }

    const record2 = new Map();
    for (let i = 0; i < s1.length; i++) {
        if (!record2.has(s2[i])) {
            record2.set(s2[i], 0);
        }

        record2.set(s2[i], record2.get(s2[i]) + 1);
    }

    if (isMapEqual(record1, record2)) {
        return true;
    }

    for (let i = len1; i < len2; i++) {
        record2.set(s2[i], (record2.get(s2[i]) || 0) + 1);
        record2.set(s2[i - len1], record2.get(s2[i - len1]) - 1);
        if (record2.get(s2[i - len1]) === 0) {
            record2.delete(s2[i - len1]);
        }

        if (isMapEqual(record1, record2)) {
            return true;
        }
    }

    return false;
};

  由于 s1 和 s2 中仅包含小写字母,那么可以利用 Unicode 结合数组来记录字符的个数,并且由于小写字母的 Unicode 是升序的,所以维护两个字符串 Unicode 的数组也是升序的,那么只需要比较两个数组的序列化字符串,即可判断 s1 是否当前字符串的子排列:

const checkInclusion = function(s1, s2) {
    const len1 = s1.length;
    const len2 = s2.length;
    if (len1 > len2) {
        return false;
    }

    const record1 = Array(26).fill(0);
    const record2 = Array(26).fill(0);

    for (let i = 0; i < len1; i++) {
        ++record1[s1[i].charCodeAt() - 97];
        ++record2[s2[i].charCodeAt() - 97];
    }

    if (record1.toString() === record2.toString()) {
        return true;
    }

    for (let i = len1; i < len2; i++) {
        ++record2[s2[i].charCodeAt() - 97];
        --record2[s2[i - len1].charCodeAt() - 97];

        if (record1.toString() === record2.toString()) {
            return true;
        }
    }

    return false;
}

写在最后

  「感谢您能耐心地读到这里,如果本文对您有帮助,欢迎点赞、分享、或者关注下方的公众号哟。」

  相关链接:

  • 《3. 无重复字符的最长子串》 https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

  • 《567. 字符串的排列》 https://leetcode-cn.com/problems/permutation-in-string/

d74005ad2b501304ad30ef5890c8aee9.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值