解法
统计s1中字符出现次数countOfLetters
,使用双指针,维护s2中双指针之间的字符的出现次数tmpCountOfLetters
以及s1中未被访问到的字符数countLeft
。对于双指针i,j,后者用来考察在s1中出现的字符,前者用来将不满足的字符剔除考察范围。
j指针前移过程:
若 s2[j] 在 s1 中出现过,且当前出现次数小于s1中出现次数
将 j 处字符的当前出现次数加一 tmpCntOfLetters[s2[j]]++
未被访问字符数减一 cntLeft--
j前移
继续循环
i指针前移
若 s2[j] 未在 s1 中出现过
将 i 向前移动到 j 的位置
移动时将 i 处字符出现次数减一 tmpCntOfLetters[s2[i]]--
将 s1 中未被访问的字符数加一 cntLeft++
否则,若 s2[j] 在 s1 中出现过,但当前出现次数大于 s1 中次数
将 i 前移一位,同样更新 tmpCntOfLetters 和 countLeft
其中比较关键的点:
- 若j遇到未在s1中出现过的字符,i和j都前移到j+1
j之前不可能出现满足条件的(会直接终止),且j处字符一定不会在所求区间内(未在s1出现)
- 若j遇到在s1中出现但超过出现次数的字符,i前移1,此后继续考察j位置的字符
因为i处的字符必定是在s1中出现过的,所以需要慎重将其从考察范围内剔除
- i前移过程不会出现条件满足的情况,假设出现了满足条件的,那么一定会在该情况处立即终止
代码
class Solution {
public:
bool checkInclusion(string s1, string s2) {
if (s1.size() > s2.size())
return false;
int cntOfLetters[26] = {};
for (auto &&ch : s1)
cntOfLetters[ch - 'a']++;
int tmpCntOfLetters[26] = {};
int cntLeft = (int)s1.size();
int i = 0, j = 0;
while (j < (int)s2.size()) {
while (j < (int)s2.size() && tmpCntOfLetters[s2[j] - 'a'] < cntOfLetters[s2[j] - 'a']) {
tmpCntOfLetters[s2[j] - 'a']++;
cntLeft--;
j++;
}
if (cntLeft == 0)
return true;
if (j == (int)s2.size())
return false;
if (cntOfLetters[s2[j] - 'a'] == 0) { // move i here
while (i <= j) {
if (tmpCntOfLetters[s2[i] - 'a'] > 0) {
tmpCntOfLetters[s2[i] - 'a']--;
cntLeft++;
}
i++;
}
j++;
} else { //
tmpCntOfLetters[s2[i] - 'a']--;
cntLeft++;
i++;
}
}
return false;
}
};