Permutation in String
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 a substring of s2.
Example:
Input:
s1 = "ab", s2 = "eidbaooo"
Expected output:
true
Explanation: s2 contains "ba", which is a permutation of "ab".
If you would like to solve the problem on LeetCode, here is the link to the problem: LeetCode problem link
Approach 1: Sliding window + hash maps
Track how many character frequencies currently match between the window and s1. Slide a window of length len(s1) over s2 and return true when every distinct character in s1 is matched.
Time: O(n) where n = len(s2)
Space: O(1) in practice (bounded by distinct characters)
func checkInclusion(s1 string, s2 string) bool {
if len(s1) > len(s2) {
return false
}
s1Map := make(map[byte]int)
for i := 0; i < len(s1); i++ {
s1Map[s1[i]]++
}
s2Map := make(map[byte]int)
found := 0
i, j := 0, 0
// Build initial window of size len(s1)-1
for j < len(s1)-1 {
ch := s2[j]
if s2Map[ch] == s1Map[ch] {
found--
}
s2Map[ch]++
if s2Map[ch] == s1Map[ch] {
found++
}
j++
}
// Slide the window
for j < len(s2) {
// Add right character
right := s2[j]
if s2Map[right] == s1Map[right] {
found--
}
s2Map[right]++
if s2Map[right] == s1Map[right] {
found++
}
// Check if all frequencies match
if found == len(s1Map) {
return true
}
// Remove left character
left := s2[i]
if s2Map[left] == s1Map[left] {
found--
}
s2Map[left]--
if s2Map[left] == 0 {
delete(s2Map, left)
}
if s2Map[left] == s1Map[left] {
found++
}
i++
j++
}
return false
}
Approach 2: Sliding window + fixed frequency arrays
Because the alphabet is lowercase English letters, use two [26]int counts. Fill the first window of length len(s1), then slide by adding the incoming character and removing the outgoing one. Array equality means the window is a permutation of s1.
Time: O(n) where n = len(s2) (comparing 26 counts is O(1))
Space: O(1)
func checkInclusion(s1 string, s2 string) bool {
if len(s1) > len(s2) {
return false
}
var cnt1, cnt2 [26]int
for i := 0; i < len(s1); i++ {
cnt1[s1[i]-'a']++
cnt2[s2[i]-'a']++
}
if cnt1 == cnt2 {
return true
}
for i := len(s1); i < len(s2); i++ {
cnt2[s2[i]-'a']++
cnt2[s2[i-len(s1)]-'a']--
if cnt1 == cnt2 {
return true
}
}
return false
}
Which approach to use?
- Prefer Approach 2 for lowercase a–z input — shorter and clearer.
- Prefer Approach 1 when the alphabet is larger or not limited to 26 letters, or when you want an explicit “matched character count” window pattern (same idea as many anagram sliding-window solutions).
