Problem
Given two strings s1
and s2
, return true
if s2
contains a permutation of s1
, or false
otherwise.
In other words, return true
if one of s1
’s permutations is the substring of s2
.
Examples
Example 1:
Input: s1 = "ab", s2 = "eidbaooo"
Output: true
Explanation: s2 contains one permutation of s1 ("ba").
Example 2:
Input: s1 = "ab", s2 = "eidboaoo"
Output: false
Solution
Method 1 - Sliding Window and 2 counting arrays
Here is the approach:
- Character Frequency Count:
- First, compute the frequency of each character in
s1
and store it in an array or counter. This array will have a fixed size of 26 to represent each lowercase English letter.
- First, compute the frequency of each character in
- Initial Window Setup:
- Next, consider the first window of length equal to
s1
ins2
and compute the frequency of characters in this window. This window represents the first possible substring ofs2
that we will test to see if it matchess1
.
- Next, consider the first window of length equal to
- Window Sliding Technique:
- Compare the frequency array of the first window in
s2
with the frequency array ofs1
. If they are equal, it means a permutation ofs1
is found ins2
, so returntrue
. - If not, slide the window one character to the right. For each slide:
- Increment the count of the new character added to the window.
- Decrement the count of the character that is no longer in the window.
- Compare the updated frequency array for the new window with
s1
’s frequency array.
- If at any point the frequency arrays match, return
true
.
- Compare the frequency array of the first window in
- Completion:
- If no matching window is found by the time we slide through the entire
s2
, returnfalse
as no permutation ofs1
is a substring ofs2
.
- If no matching window is found by the time we slide through the entire
Code
Java
public class Solution {
public boolean checkInclusion(String s1, String s2) {
if (s1.length() > s2.length()) {
return false;
}
int[] s1Count = new int[26];
int[] s2Count = new int[26];
// Count frequency of each character in s1
for (char c : s1.toCharArray()) {
s1Count[c - 'a']++;
}
// Initial window count for s2
for (int i = 0; i < s1.length(); i++) {
s2Count[s2.charAt(i) - 'a']++;
}
// Compare arrays of the first window
if (Arrays.equals(s1Count, s2Count)) {
return true;
}
// Slide the window over s2
for (int i = s1.length(); i < s2.length(); i++) {
s2Count[s2.charAt(i) - 'a']++;
s2Count[s2.charAt(i - s1.length()) - 'a']--;
if (Arrays.equals(s1Count, s2Count)) {
return true;
}
}
return false;
}
}
Python
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
if len(s1) > len(s2):
return False
s1_count = Counter(s1)
s2_count = Counter(s2[:len(s1)])
if s1_count == s2_count:
return True
for i in range(len(s1), len(s2)):
s2_count[s2[i]] += 1
s2_count[s2[i - len(s1)]] -= 1
if s2_count[s2[i - len(s1)]] == 0:
del s2_count[s2[i - len(s1)]]
if s1_count == s2_count:
return True
return False
Complexity
- ⏰ Time complexity:
O(n)
, wheren
is the length ofs2
. This is because we iterate through thes2
string a single time while maintaining a frequency count which is anO(1)
operation due to the constant size of the alphabet (26 letters). - 🧺 Space complexity:
O(1)
, because we’re using fixed-size arrays, to store the frequency of characters, which do not scale with input size.
Method 2 - Sliding window and 1 counting array
- Permutation Check: To verify if string
p
is a permutation of strings
, ensure every character inp
exists ins
. We can represent all permutations ofs
using a frequency map (Character -> Count). For example,abba
becomes{a:2, b:2}
. Given that there are only 26 lowercase letters, an array can efficiently represent this map. - Containment Check: To determine if
s2
contains a permutation ofs1
, use a sliding window of lengths1
acrosss2
. As characters enter the window from the right, decrease their count in the map; as they exit from the left, increase their count. When all counts in the map reach zero, it indicates a matching frequency of characters betweens
Code
Java
class Solution {
public boolean checkInclusion(String s1, String s2) {
int len1 = s1.length(), len2 = s2.length();
if (len1 > len2) {
return false;
}
int[] count = new int[26];
for (int i = 0; i < len1; i++) {
count[s1.charAt(i) - 'a']++;
count[s2.charAt(i) - 'a']--;
}
if (allZero(count)) {
return true;
}
for (int i = len1; i < len2; i++) {
count[s2.charAt(i) - 'a']--;
count[s2.charAt(i - len1) - 'a']++;
if (allZero(count)) {
return true;
}
}
return false;
}
private boolean allZero(int[] count) {
for (int i = 0; i < 26; i++) {
if (count[i] != 0) return false;
}
return true;
}
}
Python
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
len1, len2 = len(s1), len(s2)
if len1 > len2:
return False
count = [0] * 26 # Array to store character counts
# Initialize the count array with the first window
for i in range(len1):
count[ord(s1[i]) - ord('a')] += 1
count[ord(s2[i]) - ord('a')] -= 1
if self.allZero(count):
return True
# Slide the window over s2
for i in range(len1, len2):
count[ord(s2[i]) - ord('a')] -= 1
count[ord(s2[i - len1]) - ord('a')] += 1
if self.allZero(count):
return True
return False
def allZero(self, count):
for x in count:
if x != 0:
return False
return True
Complexity
- ⏰ Time complexity:
O(n)
- 🧺 Space complexity:
O(1)
, as the count array size is fixed at 26, which is a constant. The space does not grow with the input size.
Method 3 - Using 1 count array and sliding window technique reducing need to match 26 chars explicitly
Code
Java
public class Solution {
public boolean checkInclusion(String s1, String s2) {
int[] count = new int[26]; // Array to hold the count of characters in s1
for (char c : s1.toCharArray()) {
count[c - 'a']++;
}
int l = 0, r = 0;
int m = s1.length(), n = s2.length();
int diffs = m; // Number of differing characters
// Iterate over s2 with two pointers
while (r < s2.length()) {
// Decrement the count of the character entering the window
if (count[s2.charAt(r++) - 'a']-- > 0) {
diffs--;
}
// If diffs is zero, we found a valid permutation match
if (diffs == 0) {
return true;
}
// When the window size exceeds s1.length(), update the left pointer
if (r - l == m && count[s2.charAt(l++) - 'a']++ >= 0) {
diffs++;
}
}
return false;
}
}
Python
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
count = [0] * 26 # Array to hold the count of characters in s1
for c in s1:
count[ord(c) - ord('a')] += 1
l, r = 0, 0
diffs = len(s1) # Number of differing characters
# Iterate over s2 with two pointers
while r < len(s2):
# Decrement the count of the character entering the window
if count[ord(s2[r]) - ord('a')] > 0:
diffs -= 1
count[ord(s2[r]) - ord('a')] -= 1
r += 1
# If diffs is zero, we found a valid permutation match
if diffs == 0:
return True
# When the window size exceeds s1.length(), update the left pointer
if r - l == len(s1):
if count[ord(s2[l]) - ord('a')] >= 0:
diffs += 1
count[ord(s2[l]) - ord('a')] += 1
l += 1
return False
Complexity
- ⏰ Time complexity:
O(n)
- 🧺 Space complexity:
O(1)