【LeetCode-567】字符串的排列

问题

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。

示例

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

解答1:固定长度滑动窗口

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int m = s1.size(), n = s2.size();
        if (m > n) return 0;
        vector<int> s1_memo(26), s2_memo(26);
        for (int i = 0; i < m; i++) {
            s1_memo[s1[i] - 'a']++;
            s2_memo[s2[i] - 'a']++;
        }
        for (int i = m; i < n; i++) {
            if (s1_memo == s2_memo) return 1;
            s2_memo[s2[i] - 'a']++;
            s2_memo[s2[i - m] - 'a']--;
        }
        return s1_memo == s2_memo;
    }
};

重点思路

固定s2中点滑动窗口长度为s1.size(),比较该窗口与s1中字符数量是否相同。时间复杂度为O(m + 26 * n);

解答2:变长滑动窗口

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int m = s1.size(), n = s2.size();
        unordered_map<char, int> s1_memo, s2_memo;
        for (char& ch : s1) s1_memo[ch]++;
        int left = 0, right = 0;
        while (right < n) {
            char cur = s2[right++];
            if (s1_memo.count(cur)) {
                s2_memo[cur]++;
                while (s2_memo[cur] > s1_memo[cur])
                    s2_memo[s2[left++]]--;
                if (right - left == m) return 1; // 长度是否满足要求
            } else {
                left = right;
                s2_memo.clear();
            }
        }
        return 0;
    }
};

重点思路

使用变长滑动窗口可以更容易地使用cnt计数统计窗口内数字的数量,以此判断该窗口是否满足题目条件,避免了解答1中每次都需要比较26次的情况。这里使用unordered_map而不使用数组模拟的原因在于,哈希表的clear()函数耗时和哈希表内元素数量有关,为“元素数量*常数时间”,而vector没有类似的函数(虽然这样做内存和运行时间都变多了)。这种做法的拓展性也更强,见【LeetCode-30】串联所有单词的子串

posted @ 2021-04-16 11:34  tmpUser  阅读(51)  评论(0编辑  收藏  举报