Problem Statement
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.
Additional information
1 <= s1.length, s2.length <= 10^4s1ands2consist of lowercase English letters.
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
Brute Force
Intuition
A permutation of a string is simply a rearrangement of its characters. The brute force approach is to generate every possible permutation of s1 and check if any of them appear as a substring in s2.
Algorithm
- Generate all permutations of
s1. - For each permutation, check if it is a substring of
s2. - If found, return
True. - If no permutation is found after checking all, return
False.
def check_inclusion(s1: str, s2: str) -> bool:
from itertools import permutations
perms = [''.join(p) for p in permutations(s1)]
for p in perms:
if p in s2:
return True
return False
Time & Space Complexity
Time complexity:
O(n! * m)Reason: Generating all permutations of
s1(lengthn) takesO(n!). Checking each permutation ins2(lengthm) takesO(m). This is highly inefficient forn > 10.Space complexity:
O(n!)Reason: Storing all permutations.
Hash Table (Sliding Window)
Intuition
A string s2 contains a permutation of s1 if any substring of s2 has the exact same character counts as s1. We can use two Hash Tables (dictionaries) to store these counts: one for s1 and one for the current window in s2.
We slide a window of length len(s1) across s2. At each step, we update the hash table for the window (add the new character on the right, remove the old character on the left) and simply compare if window_counts == s1_counts.
Algorithm
If
len(s1) > len(s2), returnFalse.Create a Hash Map
s1_countfors1.Create a Hash Map
window_countfor the firstlen(s1)characters ofs2.If
s1_count == window_count, returnTrue.Iterate
ifromlen(s1)tolen(s2) - 1:Add the new character
s2[i]towindow_count.Remove the old character
s2[i - len(s1)]fromwindow_count. If its count drops to 0, verify we delete the key to ensure the dictionaries match perfectly.Check if
s1_count == window_count.
If no match is found after the loop, return
False.
from collections import Counter
def check_inclusion(s1: str, s2: str) -> bool:
if len(s1) > len(s2):
return False
s1_count = Counter(s1)
window_count = Counter(s2[:len(s1)])
if s1_count == window_count:
return True
for i in range(len(s1), len(s2)):
# Add the new character to the window
window_count[s2[i]] += 1
# Remove the old character from the left of the window
left_char = s2[i - len(s1)]
window_count[left_char] -= 1
if window_count[left_char] == 0:
del window_count[left_char] # Remove key to allow '==' comparison
if s1_count == window_count:
return True
return False
Time & Space Complexity
Time complexity:
O(26 * n)which simplifies toO(n).- Reason: We iterate through
s2once. In Python, comparing two dictionaries takes time proportional to the number of keys. Since the input is limited to lowercase English letters, there are at most 26 keys. Thus, the comparison isO(1).
- Reason: We iterate through
Space complexity:
O(1)- Reason: The Hash Maps store at most 26 characters (lowercase English letters).
Sliding Window (Optimal)
Intuition
Instead of generating permutations, we can characterize a permutation by its character counts. Two strings are permutations of each other if and only if they have the exact same frequency of every character.
We can use a sliding window of size len(s1) on s2. As the window slides from left to right, we update the character counts of the current window. If the counts of the window match the counts of s1, we found a permutation.
Algorithm
- If
len(s1) > len(s2), returnFalse. - Create frequency arrays (or hash maps) for
s1and the first window ofs2(lengthlen(s1)). - Compare the two frequency arrays. If they match, return
True. - Slide the window one character at a time from
len(s1)tolen(s2):
- Add the new character entering the window to the
s2count. - Remove the character leaving the window from the
s2count. - Compare the arrays again. If match, return
True.
- Return
Falseif no match is found.
def check_inclusion(s1: str, s2: str) -> bool:
if len(s1) > len(s2):
return False
s1_count = [0] * 26
s2_count = [0] * 26
for i in range(len(s1)):
s1_count[ord(s1[i]) - ord('a')] += 1
s2_count[ord(s2[i]) - ord('a')] += 1
matches = 0
for i in range(26):
if s1_count[i] == s2_count[i]:
matches += 1
l = 0
for r in range(len(s1), len(s2)):
if matches == 26:
return True
index = ord(s2[r]) - ord('a')
s2_count[index] += 1
if s1_count[index] == s2_count[index]:
matches += 1
elif s1_count[index] + 1 == s2_count[index]:
matches -= 1
index = ord(s2[l]) - ord('a')
s2_count[index] -= 1
if s1_count[index] == s2_count[index]:
matches += 1
elif s1_count[index] - 1 == s2_count[index]:
matches -= 1
l += 1
return matches == 26
Time & Space Complexity
Time complexity:
O(n)Reason: We slide the window across
s2once (O(n)). The comparisons inside the loop are constant time (26 characters).Space complexity:
O(1)Reason: The frequency arrays are fixed size (26).
Track
| Question | Difficulty | Company | Access |
|---|
OpenAI