From ba54758f69bd284c68c9856a2e4e58bc5a0a53a6 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Tue, 14 Jan 2025 08:27:47 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.0567 (#3949) No.0567.Permutation in String --- .../0567.Permutation in String/README.md | 510 ++++++------------ .../0567.Permutation in String/README_EN.md | 508 +++++++---------- .../0567.Permutation in String/Solution.cpp | 34 +- .../0567.Permutation in String/Solution.cs | 32 ++ .../0567.Permutation in String/Solution.go | 39 +- .../0567.Permutation in String/Solution.java | 36 +- .../0567.Permutation in String/Solution.py | 21 +- .../0567.Permutation in String/Solution.rs | 66 ++- .../0567.Permutation in String/Solution.ts | 53 +- .../0567.Permutation in String/Solution2.cpp | 43 -- .../0567.Permutation in String/Solution2.go | 41 -- .../0567.Permutation in String/Solution2.java | 43 -- .../0567.Permutation in String/Solution2.py | 30 -- .../0567.Permutation in String/Solution3.go | 26 - 14 files changed, 512 insertions(+), 970 deletions(-) create mode 100644 solution/0500-0599/0567.Permutation in String/Solution.cs delete mode 100644 solution/0500-0599/0567.Permutation in String/Solution2.cpp delete mode 100644 solution/0500-0599/0567.Permutation in String/Solution2.go delete mode 100644 solution/0500-0599/0567.Permutation in String/Solution2.java delete mode 100644 solution/0500-0599/0567.Permutation in String/Solution2.py delete mode 100644 solution/0500-0599/0567.Permutation in String/Solution3.go diff --git a/solution/0500-0599/0567.Permutation in String/README.md b/solution/0500-0599/0567.Permutation in String/README.md index ef8815d2d4360..219ae030a4900 100644 --- a/solution/0500-0599/0567.Permutation in String/README.md +++ b/solution/0500-0599/0567.Permutation in String/README.md @@ -57,13 +57,13 @@ tags: ### 方法一:滑动窗口 -我们观察发现,题目实际上等价于判断字符串 $s2$ 中是否存在窗口大小为 $n$,且窗口内的字符及其个数与字符串 $s1$ 相同的子串。 +我们用一个数组 $\textit{cnt}$ 记录当前需要匹配的字符及其个数,用一个变量 $\textit{need}$ 记录当前还需要匹配的字符种类数,初始时 $\textit{cnt}$ 为字符串 $\textit{s1}$ 中各字符出现次数,而 $\textit{need}$ 为 $\textit{s1}$ 中不同字符的个数。 -因此,我们先用哈希表或数组 $cnt1$ 统计字符串 $s1$ 中每个字符出现的次数,然后遍历字符串 $s2$,维护一个窗口大小为 $n$ 的滑动窗口,用哈希表或数组 $cnt2$ 统计窗口内每个字符出现的次数,当 $cnt1 = cnt2$ 时,说明窗口内的字符及其个数与字符串 $s1$ 相同,返回 `true` 即可。 +然后我们遍历字符串 $\textit{s2}$,对于每个字符,我们将其在 $\textit{cnt}$ 中的对应值减一,如果减一后的值等于 $0$,说明当前字符在 $\textit{s1}$ 中出现次数已经满足要求,我们将 $\textit{need}$ 减一。如果当前下标 $i$ 大于等于 $\textit{s1}$ 的长度,我们需要将 $\textit{s2}[i-\textit{s1}]\textit{cnt}$ 中对应值加一,如果加一后的值等于 $1$,说明当前字符在 $\textit{s1}$ 中出现次数不再满足要求,我们将 $\textit{need}$ 加一。在遍历过程中,如果 $\textit{need}$ 的值等于 $0$,说明所有字符的出现次数都满足要求,我们就找到了一个满足要求的子串,返回 $\text{true}$。 -否则,遍历结束后,返回 `false`。 +否则,如果遍历结束后没有找到满足要求的子串,我们返回 $\text{false}$。 -时间复杂度 $(n + m \times C)$,空间复杂度 $O(C)$。其中 $n$ 和 $m$ 分别为字符串 $s1$ 和 $s2$ 的长度;而 $C$ 为字符集的大小,本题中 $C=26$。 +时间复杂度 $O(m + n)$,其中 $m$ 和 $n$ 分别是字符串 $\textit{s1}$ 和 $\textit{s2}$ 的长度。空间复杂度 $O(|\Sigma|)$,其中 $\Sigma$ 是字符集,这道题中字符集为小写字母,所以空间复杂度是常数级别的。 @@ -72,15 +72,18 @@ tags: ```python class Solution: def checkInclusion(self, s1: str, s2: str) -> bool: - n = len(s1) - cnt1 = Counter(s1) - cnt2 = Counter(s2[:n]) - if cnt1 == cnt2: - return True - for i in range(n, len(s2)): - cnt2[s2[i]] += 1 - cnt2[s2[i - n]] -= 1 - if cnt1 == cnt2: + cnt = Counter(s1) + need = len(cnt) + m = len(s1) + for i, c in enumerate(s2): + cnt[c] -= 1 + if cnt[c] == 0: + need -= 1 + if i >= m: + cnt[s2[i - m]] += 1 + if cnt[s2[i - m]] == 1: + need += 1 + if need == 0: return True return False ``` @@ -90,24 +93,26 @@ class Solution: ```java class Solution { public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; + int need = 0; + int[] cnt = new int[26]; + for (char c : s1.toCharArray()) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - int[] cnt1 = new int[26]; - int[] cnt2 = new int[26]; + int m = s1.length(), n = s2.length(); for (int i = 0; i < n; ++i) { - ++cnt1[s1.charAt(i) - 'a']; - ++cnt2[s2.charAt(i) - 'a']; - } - if (Arrays.equals(cnt1, cnt2)) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2.charAt(i) - 'a']; - --cnt2[s2.charAt(i - n) - 'a']; - if (Arrays.equals(cnt1, cnt2)) { + int c = s2.charAt(i) - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2.charAt(i - m) - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } @@ -122,22 +127,26 @@ class Solution { class Solution { public: bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; + int need = 0; + int cnt[26]{}; + for (char c : s1) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - vector cnt1(26), cnt2(26); + int m = s1.size(), n = s2.size(); for (int i = 0; i < n; ++i) { - ++cnt1[s1[i] - 'a']; - ++cnt2[s2[i] - 'a']; - } - if (cnt1 == cnt2) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2[i] - 'a']; - --cnt2[s2[i - n] - 'a']; - if (cnt1 == cnt2) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } @@ -150,23 +159,28 @@ public: ```go func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt1 := [26]int{} - cnt2 := [26]int{} - for i := range s1 { - cnt1[s1[i]-'a']++ - cnt2[s2[i]-'a']++ - } - if cnt1 == cnt2 { - return true + need := 0 + cnt := [26]int{} + + for _, c := range s1 { + if cnt[c-'a']++; cnt[c-'a'] == 1 { + need++ + } } - for i := n; i < m; i++ { - cnt2[s2[i]-'a']++ - cnt2[s2[i-n]-'a']-- - if cnt1 == cnt2 { + + m, n := len(s1), len(s2) + for i := 0; i < n; i++ { + c := s2[i] - 'a' + if cnt[c]--; cnt[c] == 0 { + need-- + } + if i >= m { + c = s2[i-m] - 'a' + if cnt[c]++; cnt[c] == 1 { + need++ + } + } + if need == 0 { return true } } @@ -178,190 +192,109 @@ func checkInclusion(s1 string, s2 string) bool { ```ts function checkInclusion(s1: string, s2: string): boolean { - // 滑动窗口方案 - if (s1.length > s2.length) { - return false; - } - - const n = s1.length; - const m = s2.length; - - const toCode = (s: string) => s.charCodeAt(0) - 97; - const isMatch = () => { - for (let i = 0; i < 26; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } + let need = 0; + const cnt: number[] = Array(26).fill(0); + const a = 'a'.charCodeAt(0); + for (const c of s1) { + if (++cnt[c.charCodeAt(0) - a] === 1) { + need++; } - return true; - }; - - const arr1 = new Array(26).fill(0); - for (const s of s1) { - const index = toCode(s); - arr1[index]++; } - const arr2 = new Array(26).fill(0); + const [m, n] = [s1.length, s2.length]; for (let i = 0; i < n; i++) { - const index = toCode(s2[i]); - arr2[index]++; - } - - for (let l = 0, r = n; r < m; l++, r++) { - if (isMatch()) { + let c = s2.charCodeAt(i) - a; + if (--cnt[c] === 0) { + need--; + } + if (i >= m) { + c = s2.charCodeAt(i - m) - a; + if (++cnt[c] === 1) { + need++; + } + } + if (need === 0) { return true; } - - const i = toCode(s2[l]); - const j = toCode(s2[r]); - arr2[i]--; - arr2[j]++; } - return isMatch(); + return false; } ``` #### Rust ```rust -use std::collections::HashMap; - impl Solution { - // 测试两个哈希表是否匹配 - fn is_match(m1: &HashMap, m2: &HashMap) -> bool { - for (k, v) in m1.iter() { - if m2.get(k).unwrap_or(&0) != v { - return false; - } - } - true - } pub fn check_inclusion(s1: String, s2: String) -> bool { - if s1.len() > s2.len() { - return false; - } - let mut m1 = HashMap::new(); - let mut m2 = HashMap::new(); - // 初始化表 1 + let mut need = 0; + let mut cnt = vec![0; 26]; + for c in s1.chars() { - m1.insert(c, m1.get(&c).unwrap_or(&0) + 1); - } - let cs: Vec = s2.chars().collect(); - // 初始化窗口 - let mut i = 0; - while i < s1.len() { - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - i += 1; - } - if Self::is_match(&m1, &m2) { - return true; - } - // 持续滑动窗口,直到匹配或超出边界 - let mut j = 0; - while i < cs.len() { - m2.insert(cs[j], m2.get(&cs[j]).unwrap_or(&1) - 1); - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - j += 1; - i += 1; - if Self::is_match(&m1, &m2) { - return true; + let index = (c as u8 - b'a') as usize; + if cnt[index] == 0 { + need += 1; } + cnt[index] += 1; } - false - } -} -``` - - - - - - -### 方法二:滑动窗口优化 + let m = s1.len(); + let n = s2.len(); + let s2_bytes = s2.as_bytes(); -在方法一中,我们每次加入和移除一个字符时,都需要比较两个哈希表或数组,时间复杂度较高。我们可以维护一个变量 $diff$,表示两个大小为 $n$ 的字符串中,有多少种字符出现的个数不同。当 $diff=0$ 时,说明两个字符串中的字符个数相同。 - -时间复杂度 $O(n + m)$,空间复杂度 $O(C)$。其中 $n$ 和 $m$ 分别为字符串 $s1$ 和 $s2$ 的长度;而 $C$ 为字符集的大小,本题中 $C=26$。 + for i in 0..n { + let c = (s2_bytes[i] - b'a') as usize; + cnt[c] -= 1; + if cnt[c] == 0 { + need -= 1; + } - + if i >= m { + let c = (s2_bytes[i - m] - b'a') as usize; + cnt[c] += 1; + if cnt[c] == 1 { + need += 1; + } + } -#### Python3 + if need == 0 { + return true; + } + } -```python -class Solution: - def checkInclusion(self, s1: str, s2: str) -> bool: - n, m = len(s1), len(s2) - if n > m: - return False - cnt = Counter() - for a, b in zip(s1, s2): - cnt[a] -= 1 - cnt[b] += 1 - diff = sum(x != 0 for x in cnt.values()) - if diff == 0: - return True - for i in range(n, m): - a, b = s2[i - n], s2[i] - - if cnt[b] == 0: - diff += 1 - cnt[b] += 1 - if cnt[b] == 0: - diff -= 1 - - if cnt[a] == 0: - diff += 1 - cnt[a] -= 1 - if cnt[a] == 0: - diff -= 1 - - if diff == 0: - return True - return False + false + } +} ``` -#### Java +#### C# -```java -class Solution { - public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; - } +```cs +public class Solution { + public bool CheckInclusion(string s1, string s2) { + int need = 0; int[] cnt = new int[26]; - for (int i = 0; i < n; ++i) { - --cnt[s1.charAt(i) - 'a']; - ++cnt[s2.charAt(i) - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x != 0) { - ++diff; + + foreach (char c in s1) { + if (++cnt[c - 'a'] == 1) { + need++; } } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2.charAt(i - n) - 'a'; - int b = s2.charAt(i) - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; + + int m = s1.Length, n = s2.Length; + for (int i = 0; i < n; i++) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + need--; } - if (--cnt[a] == 0) { - --diff; + + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + need++; + } } - if (diff == 0) { + + if (need == 0) { return true; } } @@ -370,138 +303,49 @@ class Solution { } ``` -#### C++ +#### PHP -```cpp +```php class Solution { -public: - bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; - } - vector cnt(26); - for (int i = 0; i < n; ++i) { - --cnt[s1[i] - 'a']; - ++cnt[s2[i] - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x) { - ++diff; + /** + * @param String $s1 + * @param String $s2 + * @return Boolean + */ + function checkInclusion($s1, $s2) { + $need = 0; + $cnt = array_fill(0, 26, 0); + + for ($i = 0; $i < strlen($s1); $i++) { + $index = ord($s1[$i]) - ord('a'); + if (++$cnt[$index] == 1) { + $need++; } } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2[i - n] - 'a'; - int b = s2[i] - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; + + $m = strlen($s1); + $n = strlen($s2); + + for ($i = 0; $i < $n; $i++) { + $c = ord($s2[$i]) - ord('a'); + if (--$cnt[$c] == 0) { + $need--; } - if (--cnt[a] == 0) { - --diff; + + if ($i >= $m) { + $c = ord($s2[$i - $m]) - ord('a'); + if (++$cnt[$c] == 1) { + $need++; + } } - if (diff == 0) { + + if ($need == 0) { return true; } } + return false; } -}; -``` - -#### Go - -```go -func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt := [26]int{} - for i := range s1 { - cnt[s1[i]-'a']-- - cnt[s2[i]-'a']++ - } - diff := 0 - for _, x := range cnt { - if x != 0 { - diff++ - } - } - if diff == 0 { - return true - } - for i := n; i < m; i++ { - a, b := s2[i-n]-'a', s2[i]-'a' - if cnt[b] == 0 { - diff++ - } - cnt[b]++ - if cnt[b] == 0 { - diff-- - } - if cnt[a] == 0 { - diff++ - } - cnt[a]-- - if cnt[a] == 0 { - diff-- - } - if diff == 0 { - return true - } - } - return false -} -``` - - - - - - - -### 方法三 - - - -#### Go - -```go -func checkInclusion(s1 string, s2 string) bool { - need, window := make(map[byte]int), make(map[byte]int) - validate, left, right := 0, 0, 0 - for i := range s1 { - need[s1[i]] += 1 - } - for ; right < len(s2); right++ { - c := s2[right] - window[c] += 1 - if need[c] == window[c] { - validate++ - } - for right-left+1 >= len(s1) { - if validate == len(need) { - return true - } - d := s2[left] - if need[d] == window[d] { - validate-- - } - window[d] -= 1 - left++ - } - } - return false } ``` diff --git a/solution/0500-0599/0567.Permutation in String/README_EN.md b/solution/0500-0599/0567.Permutation in String/README_EN.md index dc9c2f6396048..dc2aca7a32f30 100644 --- a/solution/0500-0599/0567.Permutation in String/README_EN.md +++ b/solution/0500-0599/0567.Permutation in String/README_EN.md @@ -53,7 +53,15 @@ tags: -### Solution 1 +### Solution 1: Sliding Window + +We use an array $\textit{cnt}$ to record the characters and their counts that need to be matched, and a variable $\textit{need}$ to record the number of different characters that still need to be matched. Initially, $\textit{cnt}$ contains the character counts from the string $\textit{s1}$, and $\textit{need}$ is the number of different characters in $\textit{s1}$. + +Then we traverse the string $\textit{s2}$. For each character, we decrement its corresponding value in $\textit{cnt}$. If the decremented value equals $0$, it means the current character's count in $\textit{s1}$ is satisfied, and we decrement $\textit{need}$. If the current index $i$ is greater than or equal to the length of $\textit{s1}$, we need to increment the corresponding value in $\textit{cnt}$ for $\textit{s2}[i-\textit{s1}]$. If the incremented value equals $1$, it means the current character's count in $\textit{s1}$ is no longer satisfied, and we increment $\textit{need}$. During the traversal, if the value of $\textit{need}$ equals $0$, it means all character counts are satisfied, and we have found a valid substring, so we return $\text{true}$. + +Otherwise, if the traversal ends without finding a valid substring, we return $\text{false}$. + +The time complexity is $O(m + n)$, where $m$ and $n$ are the lengths of strings $\textit{s1}$ and $\textit{s2}$, respectively. The space complexity is $O(|\Sigma|)$, where $\Sigma$ is the character set. In this problem, the character set is lowercase letters, so the space complexity is constant. @@ -62,15 +70,18 @@ tags: ```python class Solution: def checkInclusion(self, s1: str, s2: str) -> bool: - n = len(s1) - cnt1 = Counter(s1) - cnt2 = Counter(s2[:n]) - if cnt1 == cnt2: - return True - for i in range(n, len(s2)): - cnt2[s2[i]] += 1 - cnt2[s2[i - n]] -= 1 - if cnt1 == cnt2: + cnt = Counter(s1) + need = len(cnt) + m = len(s1) + for i, c in enumerate(s2): + cnt[c] -= 1 + if cnt[c] == 0: + need -= 1 + if i >= m: + cnt[s2[i - m]] += 1 + if cnt[s2[i - m]] == 1: + need += 1 + if need == 0: return True return False ``` @@ -80,24 +91,26 @@ class Solution: ```java class Solution { public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; + int need = 0; + int[] cnt = new int[26]; + for (char c : s1.toCharArray()) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - int[] cnt1 = new int[26]; - int[] cnt2 = new int[26]; + int m = s1.length(), n = s2.length(); for (int i = 0; i < n; ++i) { - ++cnt1[s1.charAt(i) - 'a']; - ++cnt2[s2.charAt(i) - 'a']; - } - if (Arrays.equals(cnt1, cnt2)) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2.charAt(i) - 'a']; - --cnt2[s2.charAt(i - n) - 'a']; - if (Arrays.equals(cnt1, cnt2)) { + int c = s2.charAt(i) - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2.charAt(i - m) - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } @@ -112,22 +125,26 @@ class Solution { class Solution { public: bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; + int need = 0; + int cnt[26]{}; + for (char c : s1) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - vector cnt1(26), cnt2(26); + int m = s1.size(), n = s2.size(); for (int i = 0; i < n; ++i) { - ++cnt1[s1[i] - 'a']; - ++cnt2[s2[i] - 'a']; - } - if (cnt1 == cnt2) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2[i] - 'a']; - --cnt2[s2[i - n] - 'a']; - if (cnt1 == cnt2) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } @@ -140,23 +157,28 @@ public: ```go func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt1 := [26]int{} - cnt2 := [26]int{} - for i := range s1 { - cnt1[s1[i]-'a']++ - cnt2[s2[i]-'a']++ - } - if cnt1 == cnt2 { - return true + need := 0 + cnt := [26]int{} + + for _, c := range s1 { + if cnt[c-'a']++; cnt[c-'a'] == 1 { + need++ + } } - for i := n; i < m; i++ { - cnt2[s2[i]-'a']++ - cnt2[s2[i-n]-'a']-- - if cnt1 == cnt2 { + + m, n := len(s1), len(s2) + for i := 0; i < n; i++ { + c := s2[i] - 'a' + if cnt[c]--; cnt[c] == 0 { + need-- + } + if i >= m { + c = s2[i-m] - 'a' + if cnt[c]++; cnt[c] == 1 { + need++ + } + } + if need == 0 { return true } } @@ -168,186 +190,109 @@ func checkInclusion(s1 string, s2 string) bool { ```ts function checkInclusion(s1: string, s2: string): boolean { - // 滑动窗口方案 - if (s1.length > s2.length) { - return false; - } - - const n = s1.length; - const m = s2.length; - - const toCode = (s: string) => s.charCodeAt(0) - 97; - const isMatch = () => { - for (let i = 0; i < 26; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } + let need = 0; + const cnt: number[] = Array(26).fill(0); + const a = 'a'.charCodeAt(0); + for (const c of s1) { + if (++cnt[c.charCodeAt(0) - a] === 1) { + need++; } - return true; - }; - - const arr1 = new Array(26).fill(0); - for (const s of s1) { - const index = toCode(s); - arr1[index]++; } - const arr2 = new Array(26).fill(0); + const [m, n] = [s1.length, s2.length]; for (let i = 0; i < n; i++) { - const index = toCode(s2[i]); - arr2[index]++; - } - - for (let l = 0, r = n; r < m; l++, r++) { - if (isMatch()) { + let c = s2.charCodeAt(i) - a; + if (--cnt[c] === 0) { + need--; + } + if (i >= m) { + c = s2.charCodeAt(i - m) - a; + if (++cnt[c] === 1) { + need++; + } + } + if (need === 0) { return true; } - - const i = toCode(s2[l]); - const j = toCode(s2[r]); - arr2[i]--; - arr2[j]++; } - return isMatch(); + return false; } ``` #### Rust ```rust -use std::collections::HashMap; - impl Solution { - // 测试两个哈希表是否匹配 - fn is_match(m1: &HashMap, m2: &HashMap) -> bool { - for (k, v) in m1.iter() { - if m2.get(k).unwrap_or(&0) != v { - return false; - } - } - true - } pub fn check_inclusion(s1: String, s2: String) -> bool { - if s1.len() > s2.len() { - return false; - } - let mut m1 = HashMap::new(); - let mut m2 = HashMap::new(); - // 初始化表 1 + let mut need = 0; + let mut cnt = vec![0; 26]; + for c in s1.chars() { - m1.insert(c, m1.get(&c).unwrap_or(&0) + 1); - } - let cs: Vec = s2.chars().collect(); - // 初始化窗口 - let mut i = 0; - while i < s1.len() { - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - i += 1; - } - if Self::is_match(&m1, &m2) { - return true; - } - // 持续滑动窗口,直到匹配或超出边界 - let mut j = 0; - while i < cs.len() { - m2.insert(cs[j], m2.get(&cs[j]).unwrap_or(&1) - 1); - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - j += 1; - i += 1; - if Self::is_match(&m1, &m2) { - return true; + let index = (c as u8 - b'a') as usize; + if cnt[index] == 0 { + need += 1; } + cnt[index] += 1; } - false - } -} -``` - - - + let m = s1.len(); + let n = s2.len(); + let s2_bytes = s2.as_bytes(); - - -### Solution 2 + for i in 0..n { + let c = (s2_bytes[i] - b'a') as usize; + cnt[c] -= 1; + if cnt[c] == 0 { + need -= 1; + } - + if i >= m { + let c = (s2_bytes[i - m] - b'a') as usize; + cnt[c] += 1; + if cnt[c] == 1 { + need += 1; + } + } -#### Python3 + if need == 0 { + return true; + } + } -```python -class Solution: - def checkInclusion(self, s1: str, s2: str) -> bool: - n, m = len(s1), len(s2) - if n > m: - return False - cnt = Counter() - for a, b in zip(s1, s2): - cnt[a] -= 1 - cnt[b] += 1 - diff = sum(x != 0 for x in cnt.values()) - if diff == 0: - return True - for i in range(n, m): - a, b = s2[i - n], s2[i] - - if cnt[b] == 0: - diff += 1 - cnt[b] += 1 - if cnt[b] == 0: - diff -= 1 - - if cnt[a] == 0: - diff += 1 - cnt[a] -= 1 - if cnt[a] == 0: - diff -= 1 - - if diff == 0: - return True - return False + false + } +} ``` -#### Java +#### C# -```java -class Solution { - public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; - } +```cs +public class Solution { + public bool CheckInclusion(string s1, string s2) { + int need = 0; int[] cnt = new int[26]; - for (int i = 0; i < n; ++i) { - --cnt[s1.charAt(i) - 'a']; - ++cnt[s2.charAt(i) - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x != 0) { - ++diff; + + foreach (char c in s1) { + if (++cnt[c - 'a'] == 1) { + need++; } } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2.charAt(i - n) - 'a'; - int b = s2.charAt(i) - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; + + int m = s1.Length, n = s2.Length; + for (int i = 0; i < n; i++) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + need--; } - if (--cnt[a] == 0) { - --diff; + + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + need++; + } } - if (diff == 0) { + + if (need == 0) { return true; } } @@ -356,138 +301,49 @@ class Solution { } ``` -#### C++ +#### PHP -```cpp +```php class Solution { -public: - bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; - } - vector cnt(26); - for (int i = 0; i < n; ++i) { - --cnt[s1[i] - 'a']; - ++cnt[s2[i] - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x) { - ++diff; + /** + * @param String $s1 + * @param String $s2 + * @return Boolean + */ + function checkInclusion($s1, $s2) { + $need = 0; + $cnt = array_fill(0, 26, 0); + + for ($i = 0; $i < strlen($s1); $i++) { + $index = ord($s1[$i]) - ord('a'); + if (++$cnt[$index] == 1) { + $need++; } } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2[i - n] - 'a'; - int b = s2[i] - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; + + $m = strlen($s1); + $n = strlen($s2); + + for ($i = 0; $i < $n; $i++) { + $c = ord($s2[$i]) - ord('a'); + if (--$cnt[$c] == 0) { + $need--; } - if (--cnt[a] == 0) { - --diff; + + if ($i >= $m) { + $c = ord($s2[$i - $m]) - ord('a'); + if (++$cnt[$c] == 1) { + $need++; + } } - if (diff == 0) { + + if ($need == 0) { return true; } } + return false; } -}; -``` - -#### Go - -```go -func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt := [26]int{} - for i := range s1 { - cnt[s1[i]-'a']-- - cnt[s2[i]-'a']++ - } - diff := 0 - for _, x := range cnt { - if x != 0 { - diff++ - } - } - if diff == 0 { - return true - } - for i := n; i < m; i++ { - a, b := s2[i-n]-'a', s2[i]-'a' - if cnt[b] == 0 { - diff++ - } - cnt[b]++ - if cnt[b] == 0 { - diff-- - } - if cnt[a] == 0 { - diff++ - } - cnt[a]-- - if cnt[a] == 0 { - diff-- - } - if diff == 0 { - return true - } - } - return false -} -``` - - - - - - - -### Solution 3 - - - -#### Go - -```go -func checkInclusion(s1 string, s2 string) bool { - need, window := make(map[byte]int), make(map[byte]int) - validate, left, right := 0, 0, 0 - for i := range s1 { - need[s1[i]] += 1 - } - for ; right < len(s2); right++ { - c := s2[right] - window[c] += 1 - if need[c] == window[c] { - validate++ - } - for right-left+1 >= len(s1) { - if validate == len(need) { - return true - } - d := s2[left] - if need[d] == window[d] { - validate-- - } - window[d] -= 1 - left++ - } - } - return false } ``` diff --git a/solution/0500-0599/0567.Permutation in String/Solution.cpp b/solution/0500-0599/0567.Permutation in String/Solution.cpp index e5c08e7543eaf..0b1b45d074fd5 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.cpp +++ b/solution/0500-0599/0567.Permutation in String/Solution.cpp @@ -1,25 +1,29 @@ class Solution { public: bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; + int need = 0; + int cnt[26]{}; + for (char c : s1) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - vector cnt1(26), cnt2(26); + int m = s1.size(), n = s2.size(); for (int i = 0; i < n; ++i) { - ++cnt1[s1[i] - 'a']; - ++cnt2[s2[i] - 'a']; - } - if (cnt1 == cnt2) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2[i] - 'a']; - --cnt2[s2[i - n] - 'a']; - if (cnt1 == cnt2) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } return false; } -}; \ No newline at end of file +}; diff --git a/solution/0500-0599/0567.Permutation in String/Solution.cs b/solution/0500-0599/0567.Permutation in String/Solution.cs new file mode 100644 index 0000000000000..d46daa3b53664 --- /dev/null +++ b/solution/0500-0599/0567.Permutation in String/Solution.cs @@ -0,0 +1,32 @@ +public class Solution { + public bool CheckInclusion(string s1, string s2) { + int need = 0; + int[] cnt = new int[26]; + + foreach (char c in s1) { + if (++cnt[c - 'a'] == 1) { + need++; + } + } + + int m = s1.Length, n = s2.Length; + for (int i = 0; i < n; i++) { + int c = s2[i] - 'a'; + if (--cnt[c] == 0) { + need--; + } + + if (i >= m) { + c = s2[i - m] - 'a'; + if (++cnt[c] == 1) { + need++; + } + } + + if (need == 0) { + return true; + } + } + return false; + } +} diff --git a/solution/0500-0599/0567.Permutation in String/Solution.go b/solution/0500-0599/0567.Permutation in String/Solution.go index c9bfe4586b63a..671d5236980b5 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.go +++ b/solution/0500-0599/0567.Permutation in String/Solution.go @@ -1,23 +1,28 @@ func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt1 := [26]int{} - cnt2 := [26]int{} - for i := range s1 { - cnt1[s1[i]-'a']++ - cnt2[s2[i]-'a']++ - } - if cnt1 == cnt2 { - return true + need := 0 + cnt := [26]int{} + + for _, c := range s1 { + if cnt[c-'a']++; cnt[c-'a'] == 1 { + need++ + } } - for i := n; i < m; i++ { - cnt2[s2[i]-'a']++ - cnt2[s2[i-n]-'a']-- - if cnt1 == cnt2 { + + m, n := len(s1), len(s2) + for i := 0; i < n; i++ { + c := s2[i] - 'a' + if cnt[c]--; cnt[c] == 0 { + need-- + } + if i >= m { + c = s2[i-m] - 'a' + if cnt[c]++; cnt[c] == 1 { + need++ + } + } + if need == 0 { return true } } return false -} \ No newline at end of file +} diff --git a/solution/0500-0599/0567.Permutation in String/Solution.java b/solution/0500-0599/0567.Permutation in String/Solution.java index d6bf80c9e6754..afff6ecb493fc 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.java +++ b/solution/0500-0599/0567.Permutation in String/Solution.java @@ -1,26 +1,28 @@ class Solution { public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; + int need = 0; + int[] cnt = new int[26]; + for (char c : s1.toCharArray()) { + if (++cnt[c - 'a'] == 1) { + ++need; + } } - int[] cnt1 = new int[26]; - int[] cnt2 = new int[26]; + int m = s1.length(), n = s2.length(); for (int i = 0; i < n; ++i) { - ++cnt1[s1.charAt(i) - 'a']; - ++cnt2[s2.charAt(i) - 'a']; - } - if (Arrays.equals(cnt1, cnt2)) { - return true; - } - for (int i = n; i < m; ++i) { - ++cnt2[s2.charAt(i) - 'a']; - --cnt2[s2.charAt(i - n) - 'a']; - if (Arrays.equals(cnt1, cnt2)) { + int c = s2.charAt(i) - 'a'; + if (--cnt[c] == 0) { + --need; + } + if (i >= m) { + c = s2.charAt(i - m) - 'a'; + if (++cnt[c] == 1) { + ++need; + } + } + if (need == 0) { return true; } } return false; } -} \ No newline at end of file +} diff --git a/solution/0500-0599/0567.Permutation in String/Solution.py b/solution/0500-0599/0567.Permutation in String/Solution.py index 9b2118a9c87d5..00e1e79386434 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.py +++ b/solution/0500-0599/0567.Permutation in String/Solution.py @@ -1,13 +1,16 @@ class Solution: def checkInclusion(self, s1: str, s2: str) -> bool: - n = len(s1) - cnt1 = Counter(s1) - cnt2 = Counter(s2[:n]) - if cnt1 == cnt2: - return True - for i in range(n, len(s2)): - cnt2[s2[i]] += 1 - cnt2[s2[i - n]] -= 1 - if cnt1 == cnt2: + cnt = Counter(s1) + need = len(cnt) + m = len(s1) + for i, c in enumerate(s2): + cnt[c] -= 1 + if cnt[c] == 0: + need -= 1 + if i >= m: + cnt[s2[i - m]] += 1 + if cnt[s2[i - m]] == 1: + need += 1 + if need == 0: return True return False diff --git a/solution/0500-0599/0567.Permutation in String/Solution.rs b/solution/0500-0599/0567.Permutation in String/Solution.rs index f6ece5c203937..df5a2f8e0f1f2 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.rs +++ b/solution/0500-0599/0567.Permutation in String/Solution.rs @@ -1,46 +1,40 @@ -use std::collections::HashMap; - impl Solution { - // 测试两个哈希表是否匹配 - fn is_match(m1: &HashMap, m2: &HashMap) -> bool { - for (k, v) in m1.iter() { - if m2.get(k).unwrap_or(&0) != v { - return false; - } - } - true - } pub fn check_inclusion(s1: String, s2: String) -> bool { - if s1.len() > s2.len() { - return false; - } - let mut m1 = HashMap::new(); - let mut m2 = HashMap::new(); - // 初始化表 1 + let mut need = 0; + let mut cnt = vec![0; 26]; + for c in s1.chars() { - m1.insert(c, m1.get(&c).unwrap_or(&0) + 1); - } - let cs: Vec = s2.chars().collect(); - // 初始化窗口 - let mut i = 0; - while i < s1.len() { - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - i += 1; - } - if Self::is_match(&m1, &m2) { - return true; + let index = (c as u8 - b'a') as usize; + if cnt[index] == 0 { + need += 1; + } + cnt[index] += 1; } - // 持续滑动窗口,直到匹配或超出边界 - let mut j = 0; - while i < cs.len() { - m2.insert(cs[j], m2.get(&cs[j]).unwrap_or(&1) - 1); - m2.insert(cs[i], m2.get(&cs[i]).unwrap_or(&0) + 1); - j += 1; - i += 1; - if Self::is_match(&m1, &m2) { + + let m = s1.len(); + let n = s2.len(); + let s2_bytes = s2.as_bytes(); + + for i in 0..n { + let c = (s2_bytes[i] - b'a') as usize; + cnt[c] -= 1; + if cnt[c] == 0 { + need -= 1; + } + + if i >= m { + let c = (s2_bytes[i - m] - b'a') as usize; + cnt[c] += 1; + if cnt[c] == 1 { + need += 1; + } + } + + if need == 0 { return true; } } + false } } diff --git a/solution/0500-0599/0567.Permutation in String/Solution.ts b/solution/0500-0599/0567.Permutation in String/Solution.ts index f9e198a24dca2..fd554b26e47d4 100644 --- a/solution/0500-0599/0567.Permutation in String/Solution.ts +++ b/solution/0500-0599/0567.Permutation in String/Solution.ts @@ -1,43 +1,28 @@ function checkInclusion(s1: string, s2: string): boolean { - // 滑动窗口方案 - if (s1.length > s2.length) { - return false; - } - - const n = s1.length; - const m = s2.length; - - const toCode = (s: string) => s.charCodeAt(0) - 97; - const isMatch = () => { - for (let i = 0; i < 26; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } + let need = 0; + const cnt: number[] = Array(26).fill(0); + const a = 'a'.charCodeAt(0); + for (const c of s1) { + if (++cnt[c.charCodeAt(0) - a] === 1) { + need++; } - return true; - }; - - const arr1 = new Array(26).fill(0); - for (const s of s1) { - const index = toCode(s); - arr1[index]++; } - const arr2 = new Array(26).fill(0); + const [m, n] = [s1.length, s2.length]; for (let i = 0; i < n; i++) { - const index = toCode(s2[i]); - arr2[index]++; - } - - for (let l = 0, r = n; r < m; l++, r++) { - if (isMatch()) { + let c = s2.charCodeAt(i) - a; + if (--cnt[c] === 0) { + need--; + } + if (i >= m) { + c = s2.charCodeAt(i - m) - a; + if (++cnt[c] === 1) { + need++; + } + } + if (need === 0) { return true; } - - const i = toCode(s2[l]); - const j = toCode(s2[r]); - arr2[i]--; - arr2[j]++; } - return isMatch(); + return false; } diff --git a/solution/0500-0599/0567.Permutation in String/Solution2.cpp b/solution/0500-0599/0567.Permutation in String/Solution2.cpp deleted file mode 100644 index bfcaf32a285cd..0000000000000 --- a/solution/0500-0599/0567.Permutation in String/Solution2.cpp +++ /dev/null @@ -1,43 +0,0 @@ -class Solution { -public: - bool checkInclusion(string s1, string s2) { - int n = s1.size(), m = s2.size(); - if (n > m) { - return false; - } - vector cnt(26); - for (int i = 0; i < n; ++i) { - --cnt[s1[i] - 'a']; - ++cnt[s2[i] - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x) { - ++diff; - } - } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2[i - n] - 'a'; - int b = s2[i] - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; - } - if (--cnt[a] == 0) { - --diff; - } - if (diff == 0) { - return true; - } - } - return false; - } -}; \ No newline at end of file diff --git a/solution/0500-0599/0567.Permutation in String/Solution2.go b/solution/0500-0599/0567.Permutation in String/Solution2.go deleted file mode 100644 index d16d0a95f04c3..0000000000000 --- a/solution/0500-0599/0567.Permutation in String/Solution2.go +++ /dev/null @@ -1,41 +0,0 @@ -func checkInclusion(s1 string, s2 string) bool { - n, m := len(s1), len(s2) - if n > m { - return false - } - cnt := [26]int{} - for i := range s1 { - cnt[s1[i]-'a']-- - cnt[s2[i]-'a']++ - } - diff := 0 - for _, x := range cnt { - if x != 0 { - diff++ - } - } - if diff == 0 { - return true - } - for i := n; i < m; i++ { - a, b := s2[i-n]-'a', s2[i]-'a' - if cnt[b] == 0 { - diff++ - } - cnt[b]++ - if cnt[b] == 0 { - diff-- - } - if cnt[a] == 0 { - diff++ - } - cnt[a]-- - if cnt[a] == 0 { - diff-- - } - if diff == 0 { - return true - } - } - return false -} \ No newline at end of file diff --git a/solution/0500-0599/0567.Permutation in String/Solution2.java b/solution/0500-0599/0567.Permutation in String/Solution2.java deleted file mode 100644 index 751eab8186779..0000000000000 --- a/solution/0500-0599/0567.Permutation in String/Solution2.java +++ /dev/null @@ -1,43 +0,0 @@ -class Solution { - public boolean checkInclusion(String s1, String s2) { - int n = s1.length(); - int m = s2.length(); - if (n > m) { - return false; - } - int[] cnt = new int[26]; - for (int i = 0; i < n; ++i) { - --cnt[s1.charAt(i) - 'a']; - ++cnt[s2.charAt(i) - 'a']; - } - int diff = 0; - for (int x : cnt) { - if (x != 0) { - ++diff; - } - } - if (diff == 0) { - return true; - } - for (int i = n; i < m; ++i) { - int a = s2.charAt(i - n) - 'a'; - int b = s2.charAt(i) - 'a'; - if (cnt[b] == 0) { - ++diff; - } - if (++cnt[b] == 0) { - --diff; - } - if (cnt[a] == 0) { - ++diff; - } - if (--cnt[a] == 0) { - --diff; - } - if (diff == 0) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/solution/0500-0599/0567.Permutation in String/Solution2.py b/solution/0500-0599/0567.Permutation in String/Solution2.py deleted file mode 100644 index 445fdf4b6f29d..0000000000000 --- a/solution/0500-0599/0567.Permutation in String/Solution2.py +++ /dev/null @@ -1,30 +0,0 @@ -class Solution: - def checkInclusion(self, s1: str, s2: str) -> bool: - n, m = len(s1), len(s2) - if n > m: - return False - cnt = Counter() - for a, b in zip(s1, s2): - cnt[a] -= 1 - cnt[b] += 1 - diff = sum(x != 0 for x in cnt.values()) - if diff == 0: - return True - for i in range(n, m): - a, b = s2[i - n], s2[i] - - if cnt[b] == 0: - diff += 1 - cnt[b] += 1 - if cnt[b] == 0: - diff -= 1 - - if cnt[a] == 0: - diff += 1 - cnt[a] -= 1 - if cnt[a] == 0: - diff -= 1 - - if diff == 0: - return True - return False diff --git a/solution/0500-0599/0567.Permutation in String/Solution3.go b/solution/0500-0599/0567.Permutation in String/Solution3.go deleted file mode 100644 index f99fd03558243..0000000000000 --- a/solution/0500-0599/0567.Permutation in String/Solution3.go +++ /dev/null @@ -1,26 +0,0 @@ -func checkInclusion(s1 string, s2 string) bool { - need, window := make(map[byte]int), make(map[byte]int) - validate, left, right := 0, 0, 0 - for i := range s1 { - need[s1[i]] += 1 - } - for ; right < len(s2); right++ { - c := s2[right] - window[c] += 1 - if need[c] == window[c] { - validate++ - } - for right-left+1 >= len(s1) { - if validate == len(need) { - return true - } - d := s2[left] - if need[d] == window[d] { - validate-- - } - window[d] -= 1 - left++ - } - } - return false -} \ No newline at end of file