567. 字符串的排列 ●●
描述
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
1
<
=
s
1.
l
e
n
g
t
h
,
s
2.
l
e
n
g
t
h
<
=
1
0
4
1 <= s1.length, s2.length <= 10^4
1<=s1.length,s2.length<=104
s1 和 s2 仅包含小写字母
示例
输入:s1 = “ab” s2 = “eidbaooo”
输出:true
解释:s2 包含 s1 的排列之一 (“ba”).
题解
1. 计数 + 暴力
用一个数组记录字母在s1中出现的次数,然后遍历s2,遍历的下标作为匹配字符串的开头,更新记录数组剩余的字母个数,匹配失败则字符串开头往下移动一个,重新遍历匹配,直到n1个字符全部匹配完成。
时间复杂度: O ( n m ) O(nm) O(nm)
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n1 = s1.size(), n2 = s2.size();
if(n1 > n2) return false;
vector<int> cnt1(26, 0);
for(char ch : s1){
++cnt1[ch-'a']; // 统计字母在s1中出现的次数
}
for(int i = 0; i <= n2 - n1; ++i){
vector<int> temp(cnt1.begin(), cnt1.end()); // 临时比较数组
int n = 0; // 匹配字母数
for(int j = 0; j < n1; ++j){
if(temp[s2[i+j] - 'a'] > 0){ // 存在字母可匹配
--temp[s2[i+j] - 'a'];
++n;
}else{ // s1中已不存在该字母。匹配失败
break;
}
}
if(n == n1) return true; // 所有s1字母匹配完成
}
return false;
}
};
2. 计数 + 双指针
用一个数组统计字母在s1中出现的次数,然后用左右指针来遍历s2,通过数组元素值是否小于零来判断能否匹配,进而控制左右指针的移动。
-
时间复杂度: O ( n 1 + n 2 + ∣ Σ ∣ ) O(n_1+n_2+|\Sigma|) O(n1+n2+∣Σ∣), ∣ Σ ∣ = 26 |\Sigma| = 26 ∣Σ∣=26。
创建 cnt \textit{cnt} cnt 需要 O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣) 的时间。
遍历 s 1 s_1 s1需要 O ( n 1 ) O(n_1) O(n1) 的时间。
双指针遍历 s 2 s_2 s2 时,由于 left \textit{left} left 和 right \textit{right} right 都只会向右移动,故这一部分需要 O ( n 2 ) O(n_2) O(n2) 的时间。 -
空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣)。
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n1 = s1.size(), n2 = s2.size();
if(n1 > n2) return false;
vector<int> cnt1(26, 0);
for(char ch : s1){
++cnt1[ch-'a']; // 统计字母在s1中出现的次数
}
int left = 0, right = 0;
while(right < n2){
int index = s2[right]-'a'; // 右指针上的字符索引
--cnt1[index];
while(cnt1[index] < 0){ // s1中没有剩余该字符可匹配
++cnt1[s2[left]-'a']; // 所以左指针右移
++left; // 把左指针释放的字母用于右指针来匹配 cnt1[index] == 0
}
// 如果左指针所有字母都不能匹配,那么将释放到右指针处,此时 left = right + 1, cnt1[index] == 0
if(right-left+1 == n1) return true;
++right;
// cout << left << "--" << right << endl;
}
return false;
}
};
3. 滑动窗口
用一个数组统计s1中字母的个数,然后通过滑动s1长度的窗口,维护进入和移出的字母的个数来判断是否匹配成功。