【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】串联所有单词的子串。