|
| 1 | + |
| 2 | +# Problem: Permutation in a String |
| 3 | + |
| 4 | +LeetCode problem: [567. Permutation in String](https://leetcode.com/problems/permutation-in-string/). |
| 5 | + |
| 6 | +Given a pattern and a string, find out if the string contains any permutation of the pattern. |
| 7 | + |
| 8 | +Permutation is defined as the re-arranging of the characters of the string. For example, `abc` has the following six permutations: `abc`, `acb`, `bac`, `bca`, `cab`, `cba`. |
| 9 | + |
| 10 | +## Examples |
| 11 | + |
| 12 | +Example 1: |
| 13 | + |
| 14 | +```plaintext |
| 15 | +Input: Pattern = "abc", String = "oidbcaf" |
| 16 | +Output: true |
| 17 | +Explanation: The string contains "bca" which is a permutation of the given pattern. |
| 18 | +``` |
| 19 | + |
| 20 | +Example 2: |
| 21 | + |
| 22 | +```plaintext |
| 23 | +Input: Pattern = "dc", String = "odicf" |
| 24 | +Output: false |
| 25 | +Explanation: No permutation of the pattern is present in the given string as a substring. |
| 26 | +``` |
| 27 | + |
| 28 | +Example 3: |
| 29 | + |
| 30 | +```plaintext |
| 31 | +Input: Pattern = "bcdyabcdx", String = "bcdxabcdy" |
| 32 | +Output: true |
| 33 | +Explanation: Both the string and the pattern are a permutation of each other. |
| 34 | +``` |
| 35 | + |
| 36 | +Example 4: |
| 37 | + |
| 38 | +```plaintext |
| 39 | +Input: Pattern = "abc", String = "aaacb" |
| 40 | +Output: true |
| 41 | +Explanation: The string contains "acb" which is a permutation of the given pattern. |
| 42 | +``` |
| 43 | + |
| 44 | +## Solution |
| 45 | + |
| 46 | +The algorithm uses a sliding window of the same length as the input pattern. It maintains a frequency counter of the pattern's characters and compares this to the frequency of characters in the sliding window of the target string as the window moves over the string. |
| 47 | + |
| 48 | +Here are the steps of the algorithm: |
| 49 | + |
| 50 | +1. Create a counter to track the frequencies of all characters in the pattern string. |
| 51 | +2. Iterate through the target string, adding one character at a time in the sliding window while maintaining a window of the same length as the pattern. |
| 52 | +3. For each new character added to the window, decrease its frequency in the counter if it matches a character in the pattern. If its frequency reaches zero, that character is fully matched. |
| 53 | +4. If at any time, the number of matched characters equals the number of unique characters in the pattern string, the window contains a permutation of the pattern. |
| 54 | +5. When the window exceeds the pattern's length, the window is shrunk by removing the leftmost character, restoring its frequency in the counter if it belongs to the pattern and adjust the matched count accordingly. |
| 55 | + |
| 56 | +Complexity analysis: |
| 57 | + |
| 58 | +- Time complexity: O(N + M) |
| 59 | +- Space complexity: O(N) or O(1) if `s1` and `s2` consist of lowercase English letters only. |
| 60 | + |
| 61 | +Where: |
| 62 | + |
| 63 | +- `N` is the length of `s1` (the pattern string). |
| 64 | +- `M` is the length of `s2` (the target string). |
| 65 | + |
| 66 | +```python |
| 67 | +def checkInclusion(s1: str, s2: str) -> bool: |
| 68 | + # O(N) time and O(N) space |
| 69 | + s1_counter = Counter(s1) |
| 70 | + |
| 71 | + window_start = 0 |
| 72 | + window_matched_characters = 0 |
| 73 | + for window_end in range(len(s2)): |
| 74 | + window_end_character = s2[window_end] |
| 75 | + |
| 76 | + # if the character is part of s1, decrease its frequency in the counter |
| 77 | + if window_end_character in s1_counter: |
| 78 | + s1_counter[window_end_character] -= 1 |
| 79 | + |
| 80 | + # if the frequency becomes zero, it means we have matched all instances of this character |
| 81 | + if s1_counter[window_end_character] == 0: |
| 82 | + window_matched_characters += 1 |
| 83 | + |
| 84 | + # if all characters from s1 are matched, return True |
| 85 | + if window_matched_characters == len(s1_counter): |
| 86 | + return True |
| 87 | + |
| 88 | + # shrink the window when its length is longer than the pattern length |
| 89 | + if window_end >= len(s1) - 1: |
| 90 | + window_start_character = s2[window_start] |
| 91 | + |
| 92 | + # if the character that is leaving is part of s1, increment its frequency back in the counter |
| 93 | + if window_start_character in s1_counter: |
| 94 | + # if the character was previously fully matched, we lose the match for it |
| 95 | + if s1_counter[window_start_character] == 0: |
| 96 | + window_matched_characters -= 1 |
| 97 | + s1_counter[window_start_character] += 1 |
| 98 | + |
| 99 | + window_start += 1 |
| 100 | + |
| 101 | + return False |
| 102 | +``` |
0 commit comments