The problem
Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.
A permutation is a rearrangement of all the characters of a string.
In other words, return true if one of s1’s permutations is a substring of s2.
Examples
Input: s1 = "ab", s2 = "eidbaooo"
Output: true
Explanation: s2 contains one permutation of s1 ("ba").
Input: s1 = "ab", s2 = "eidboaoo"
Output: false
Constraints
- 1 <= s1.length, s2.length <= 10^4
- s1 and s2 consist of lowercase English letters.
Explanation
Before we jump into the solution, let’s take a look at the example with input s1 = "ab", s2 = "eidbaooo"
We are looking for a permutation of s1 in s2 with the size of s1.
In the example, we can see that we have a permutation of s1 = "ab" in s2 = "eidbaooo" but in a different order.
If we had, for example, characters
"ab"ins2, it would also count as a permutation. Order in this case does not matter.
Hash Map Solution
func checkInclusion(_ s1: String, _ s2: String) -> Bool {
let s2Len = s2.count
let s2Array = Array(s2)
var count1: [Character: Int] = [:]
for c in s1 {
count1[c, default: 0] += 1
}
let need = count1.count
for i in 0 ..< s2Len {
var count2 : [Character: Int] = [:]
var curr = 0
for j in i ..< s2Len {
count2[s2Array[j], default: 0] += 1
if count1[s2Array[j], default: 0] < count2[s2Array[j], default: 0] {
break
}
if count1[s2Array[j], default: 0] == count2[s2Array[j]] {
curr += 1
}
if curr == need {
return true
}
}
}
return false
}
Explanation
We can solve this problem by using a hashmap.
- We are going to create a hashmap for
s1which will stay the same. - We are also going to have a hashmap for
s2which will contain only the characters of the current window. So, every time we create a window, we only add the new character and maybe remove the character that was on the left. - Once we have those hashmaps, we are going to compare them to check if they are equal.
This solution will take O(n * m) time complexity, but we can optimize it further and get O(n) time.
Time/ Space complexity
- Time complexity: O(n * m)
- Space complexity: O(m)
- Where
nis the length of string2, andmis the length of string1.
Sliding Window Solution
func checkInclusion(_ s1: String, _ s2: String) -> Bool {
if s1.count > s2.count {
return false
}
let s1Array = Array(s1)
let s2Array = Array(s2)
let aAsciiValue: Int = Int(Character("a").asciiValue!)
var count1 = Array(repeating: 0, count: 26)
var count2 = Array(repeating: 0, count: 26)
for i in 0 ..< s1.count {
count1[Int(s1Array[i].asciiValue!) - aAsciiValue] += 1
count2[Int(s2Array[i].asciiValue!) - aAsciiValue] += 1
}
var matches = 0
for i in 0 ..< 26 {
if count1[i] == count2[i] {
matches += 1
}
}
var l = 0
for r in s1.count ..< s2.count {
if matches == 26 {
return true
}
var index = Int(s2Array[r].asciiValue!) - aAsciiValue
count2[index] += 1
if count1[index] == count2[index] {
matches += 1
}
if count1[index] + 1 == count2[index] {
matches -= 1
}
index = Int(s2Array[l].asciiValue!) - aAsciiValue
count2[index] -= 1
if count1[index] == count2[index] {
matches += 1
}
if count1[index] - 1 == count2[index] {
matches -= 1
}
l += 1
}
return matches == 26
}
Explanation
From the previous solution, we learned that we can get O(n) time with some optimizations.
- We are going to have the same hashmaps that we had in the previous solution.
- We are also going to have a window of the size of string1, which we will move to the right by one.
The difference between this solution and the previous one is that we are not going to compare two hashmaps directly. Instead, we are going to keep track of one more variable, matches.
The matches variable maintains the total number of equal character counts in both hashmaps.
So, the first thing we do is look at the first few characters and fill up our s2Count hashmap.
- After we fill our hashmaps, we iterate through both hashmaps comparing each character.
- Next, we can see that both hashmaps have matches because they both have zero
ds,es,fs, etc. - They all match every single character except for
candx. Therefore, we initially have24 matches.
Next, we shift our window one character to the right.
- When we shift, we remove the
bcharacter from thes2Counthashmap. - Now, changing the count of
bins2Countto0means it no longer matches thebcount in thes1Counthashmap. Therefore, ourmatchestotal is updated to23. - We also added a new character
y, which affects our matches because ins1Countwe have zeroys. This means we created a mismatch and need to decrease ourmatchesby1, making the total number ofmatches = 22.
Next, we continue moving our window and repeat the operations above until we reach 26 matches, at which point we stop the algorithm and return true, or until we reach the end of string2, in which case we return false.
We also return false if string2 is shorter than string1.
Time/ Space complexity
- Time complexity: O(n)
- Space complexity: O(1)