9.找到字符串中所有字母异位词

75 阅读2分钟

题目链接

给定两个字符串 sp,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例 2:

输入: s = "abab", p = "ab" 输出: [0,1,2] 解释: 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。 起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。 起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

题解1 暴力解法

思路

其实和上一题类似,就是遍历每个字母为起始位,然后向后遍历 p 个字符,与 p 做计数比较,相等则加入到集合里。

暴力解法会超时,但还是把代码贴一下。

代码

function findAnagrams(s: string, p: string): number[] {
    const isSame = (str1: string, str2: string): boolean => {
        if (str1.length !== str2.length) return false;

        const count = {};

        for (let char of str1) {
            count[char] = (count[char] || 0) + 1;
        }

        for (let char of str2) {
            if (!count[char]) {
                return false;
            } else {
                count[char]--;
            }
        }

        return true;
    }

    const result: number[] = [];
    for (let i = 0; i <= s.length - p.length; i++) {
        if (isSame(s.slice(i, i + p.length), p)) {
            result.push(i);
        }
    }

    return result;
};

时空复杂度分析

时间复杂度:s的长度为n, p的长度为m,时间复杂度为 O(nm)

空间复杂度:isSame的空间复杂度是常数,因为最多只有26个字母,而slice会创建一个新的字符串,所以空间复杂度为O(m)

题解2 滑动窗口

思路

暴力的优化点和上一题类似,要优化重复计算。

在需要经常比较两个字符串是否相等,可以用两个数组统计 ascii 码的次数,然后使用 toString 方法来比较相等。

right 指针一直往后遍历到结尾,将每个字符的的ascii 码加入到数组中,如果当前窗口大于 p 的长度,那么将 left 指针收紧。

代码

function findAnagrams(s: string, p: string): number[] {
    const pCount: number[] = Array(26).fill(0);
    const sCount: number[] = Array(26).fill(0);
    const aCharCode: number = 'a'.charCodeAt(0);

    for (let i = 0; i < p.length; i++) {
        pCount[p.charCodeAt(i) - aCharCode]++; // 统计 p 中每个字符的频率
    }

    const result: number[] = [];
    let left: number = 0;
    let right: number = 0;
    while (right < s.length) {
        // 扩展窗口,添加右边的字符
        sCount[s.charCodeAt(right) - aCharCode]++;

        // 当窗口大小超过 p 的长度时,左边收缩窗口
        if (right - left + 1 > p.length) {
            sCount[s.charCodeAt(left) - aCharCode]--;
            left++;
        }

        // 判断当前窗口是否是异位词
        if (pCount.toString() === sCount.toString()) {
            result.push(left);
        }

        right++;
    }

    return result;
};

时空复杂度分析

时间复杂度:只遍历了一次字符串,所以是 O(n)

空间复杂度:使用了常量数组,复杂度为 O(1)