Opening the reading…
Opening the reading…
SLIDING WINDOW › FIXED SIZE SLIDING-WINDOW
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →The order of characters inside a permutation does not matter. What matters is how many times each lowercase letter appears. For example, ab and ba have different orders but identical counts: one a, one b, and zero of every other letter. Therefore, a substring of s2 is a valid match exactly when its character counts equal the counts of s1.
Every candidate substring must have length s1.length(), so inspect only windows of that fixed size. When the window moves one position right, one character leaves from the left and one character enters on the right. Updating those two counts is enough; rebuilding the entire window would repeat work that the previous window has already done.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The first window takes O(m) updates, and each of the remaining n - m positions causes one increment, one decrement, and a comparison of 26 entries. Since 26 is constant and m is at most n, the total is O(n), including the worst case where no window matches and the entire string is scanned. |
| Space | O(1) extra | The two frequency arrays always contain exactly 26 integers, regardless of input length. The returned boolean is output rather than working memory, so it is excluded; the extra space does not degrade for any input shape. |
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
bool checkInclusion(string s1, string s2) {
if (s1.length() > s2.length()) {
return false;
}
vector<int> required(26, 0);
vector<int> window(26, 0);
for (char c : s1) {
required[c - 'a']++;
}
for (int i = 0; i < static_cast<int>(s1.length()); ++i) {
window[s2[i] - 'a']++;
}
if (required == window) {
return true;
}
for (int right = static_cast<int>(s1.length()); right < static_cast<int>(s2.length()); ++right) {
window[s2[right] - 'a']++;
window[s2[right - static_cast<int>(s1.length())] - 'a']--;
if (required == window) {
return true;
}
}
return false;
}
};The subtraction index is the key placement detail. When right is the index of the newly entered character, right - s1.length() is the old left edge of the window. Adding s2[right] before subtracting the outgoing character keeps the current window represented exactly, while the equality check immediately after the update tests that complete window.
Comparing two 26-element vectors is already constant work, but you can avoid even that fixed scan by tracking how many letters currently have the wrong count. Adjust that number whenever an entry changes from equal to unequal or from unequal to equal. This keeps the same O(n) time and O(1) extra space; it only makes each slide do less constant work.
#include <string>
#include <vector>
using namespace std;
class Solution {
void change(vector<int>& difference, int index, int amount, int& mismatched) {
if (difference[index] != 0) {
--mismatched;
}
difference[index] += amount;
if (difference[index] != 0) {
++mismatched;
}
}
public:
bool checkInclusion(string s1, string s2) {
if (s1.length() > s2.length()) {
return false;
}
vector<int> difference(26, 0);
for (char c : s1) {
++difference[c - 'a'];
}
int mismatched = 0;
for (int i = 0; i < 26; ++i) {
if (difference[i] != 0) {
++mismatched;
}
}
for (int i = 0; i < static_cast<int>(s1.length()); ++i) {
change(difference, s2[i] - 'a', -1, mismatched);
}
if (mismatched == 0) {
return true;
}
for (int right = static_cast<int>(s1.length()); right < static_cast<int>(s2.length()); ++right) {
change(difference, s2[right] - 'a', -1, mismatched);
change(difference, s2[right - static_cast<int>(s1.length())] - 'a', 1, mismatched);
if (mismatched == 0) {
return true;
}
}
return false;
}
};Here difference stores required count minus current-window count. The helper updates mismatched around each change: it first removes the old status, applies the count change, then adds the new status. This version is an optimisation rather than a new algorithm. The direct vector comparison is often easier to read, while this version is useful when the alphabet is larger or comparisons are no longer tiny.