leetcode:567. 字符串的排列
题目
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。
换句话说,s1
的排列之一是 s2
的 子串 。
示例:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
输入:s1= "ab" s2 = "eidboaoo"
输出:false
解答 & 代码
滑动窗口写法一
滑动窗口,思路同:
[困难] 76. 最小覆盖子串
只有两点区别:
- 窗口左侧收缩的时机,变为窗口大小 >=
s1
长度时。因为窗口要满足条件,就得是s1
的一个排列,长度就应该等于s1
。右边界扩张到窗口大小 >=s1
长度时,如果不收缩下左边界,右边界继续扩张,窗口继续变大,不可能满足条件 在收缩窗口时判定是否找到了合法的窗口,即
validChCnt == needs.size()
(此时等长,且包含字符相同,所以窗口是s1
的一个排列)class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char, int> needs;
unordered_map<char, int> window;
for(int i = 0; i < s1.size(); ++i)
++needs[s1[i]];
int left = 0;
int right = 0;
int validChCnt = 0;
while(right < s2.size())
{
char c = s2[right];
++right;
if(needs.find(c) != needs.end())
{
++window[c];
if(window[c] == needs[c])
++validChCnt;
}
// 变化 1:窗口左侧收缩的时机,变为窗口大小 >= s1 长度时
// 因为窗口要满足条件,就得是 s1 的一个排列,长度就应该等于 s1
while(right - left >= s1.size())
{
// 变化 2:判断是否找到了合法的窗口
if(validChCnt == needs.size())
return true;
char deleteC = s2[left];
++left;
if(needs.find(deleteC) != needs.end())
{
if(window[deleteC] == needs[deleteC])
--validChCnt;
--window[deleteC];
}
}
}
return false;
}
};
复杂度分析:设字符串
s2
长为 N,字符串s1
包含 C 个不同的字符
- 时间复杂度 O(N):最坏情况下,字符串
s2
的每个字符分别被左、右指针遍历一次 - 空间复杂度 O(C):两个哈希表的空间复杂度都取决于字符串
s1
中不同字符的种数 C
执行结果:
执行结果:通过
执行用时:12 ms, 在所有 C++ 提交中击败了 42.60% 的用户
内存消耗:7.3 MB, 在所有 C++ 提交中击败了 25.09% 的用户
滑动窗口写法二 [推荐]
上面的写法改变了窗口收缩时机。
下面的写法更加模板化,窗口收缩时机不变,唯一的改变就是判定窗口是否合法
class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char, int> need;
unordered_map<char, int> window;
for(int i = 0; i < s1.size(); ++i)
++need[s1[i]];
int left = 0;
int right = 0;
int validCnt = 0;
while(right < s2.size())
{
char ch = s2[right];
++right;
if(need.find(ch) != need.end())
{
++window[ch];
if(window[ch] == need[ch])
++validCnt;
}
// 窗口收缩(左边界右移)
while(validCnt == need.size())
{
// 唯一的变化:在这里判断是否找到了合法的窗口,即窗口长度 == s1 的长度
if(right - left == s1.size())
return true;
char deleteCh = s2[left];
++left;
if(need.find(deleteCh) != need.end())
{
--window[deleteCh];
if(window[deleteCh] == need[deleteCh] - 1)
--validCnt;
}
}
}
return false;
}
};
执行结果:
执行结果:通过
执行用时:24 ms, 在所有 C++ 提交中击败了 14.64% 的用户
内存消耗:7.1 MB, 在所有 C++ 提交中击败了 71.42% 的用户