567.Permutation in String
===
###### tags: `Medium`,`String`,`Hash Table`,`Two Pointers`,`Sliding Window`
[567. Permutation in String](https://leetcode.com/problems/permutation-in-string/)
### 題目描述
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`.
### 範例
**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
```
**Constraints**:
* 1 <= `s1.length`, `s2.length` <= 10^4^
* `s1` and `s2` consist of lowercase English letters.
### 解答
#### C++
```cpp=
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n1 = s1.size(), n2 = s2.size();
vector<int> cnt1(26, 0), cnt2(26, 0);
for (auto c : s1) {
cnt1[c - 'a']++;
}
for (int i = 0; i < n2; i++) {
auto c = s2[i];
cnt2[c - 'a']++;
if (i >= n1) cnt2[s2[i - n1] - 'a']--;
if (cnt1 == cnt2) return true;
}
return false;
}
};
```
> [name=Yen-Chi Chen][time=Sat, Feb 4, 2023]
#### Python
```python=
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
n1, n2 = len(s1), len(s2)
c1, c2 = Counter(s1), Counter(s2[:n1])
if c1 == c2: return True
for i in range(n1, n2):
c2[s2[i]] += 1
c2[s2[i - n1]] -= 1
if c1 == c2: return True
return False
```
> [name=Yen-Chi Chen][time=Sat, Feb 4, 2023]
#### Javascritp
##### 思路:
一開始耍腦殘把`s1`的字母所有排列組合都列出來然後丟到`s2`去比對。
其實不用管`s1`的組合,只要找`s2`中相同長度的子字串,其中有`s1`的所有字母且各字母數一樣即可。
例:
`s1 = aabc`
`s2 = bacadbe`
`s2`的子字串中只要符合長度與`s1`相同且有2個`a`、1個`b`、1個`c`,那就一定是`s1`的排列組合之一。
1. 先記錄`s1`中各字母出現的次數(`s1Map`)
2. 遍歷`s2`,尋找長度為`s1.length`的子字串,並記錄各字母出現的次數(`s2Map`)
3. 比較`s1Map`與`s2Map`中各字母出現的次數是否相同
使用slinding window就不用每次都重新計算`s2Map`,只要把最左邊的字母刪掉,再把最右邊的字母加進去即可。
```javascript=
function checkInclusion(s1, s2) {
// 紀錄s1中每個字母出現的次數
const s1Map = new Map();
for (const char of s1) {
s1Map.set(char, s1Map.get(char) + 1 || 1);
}
const s2Map = new Map();
let left = 0; // 紀錄從何處開始比對
for (let i = 0; i < s2.length; i++) {
if (s1Map.has(s2[i])) {
s2Map.set(s2[i], s2Map.get(s2[i]) + 1 || 1);
// 當s2Map中單一字母的數量大於s1Map中單一字母的數量時,把最左邊的字母刪掉
while (s2Map.get(s2[i]) > s1Map.get(s2[i])) {
s2Map.set(s2[left], s2Map.get(s2[left]) - 1);
left++;
}
// 當s2Map中的字母數量等於s1Map中的字母數量時,檢查各字母的數量是否相同
if (s2Map.size === s1Map.size) {
let isSame = true;
for (const [key, value] of s1Map) {
if (s2Map.get(key) !== value) {
isSame = false;
break;
}
}
if (isSame) return true;
}
} else {
// 出現不一樣的字母時,直接清空s2Map
s2Map.clear();
left = i + 1;
}
}
return false;
}
```
> 寫完還是看不懂吉神寫的,怎麼可以這麼簡潔...
> [name=Marsgoat][time=Feb 16, 2023]
吉神教學版
> 吉神:我們其實只需要用一個s1大小的sliding window在s2上掃過去就好
```javascript=
function checkInclusion2(s1, s2) {
const s1Map = new Array(26).fill(0);
const s2Map = new Array(26).fill(0);
for (const char of s1) {
s1Map[char.charCodeAt() - 97]++;
}
const n = s1.length;
for (let i = 0; i < s2.length; i++) {
s2Map[s2.charCodeAt(i) - 97]++;
if (i >= n) {
s2Map[s2.charCodeAt(i - n) - 97]--;
}
if (s1Map.join() === s2Map.join()) return true;
}
return false;
}
```
> 感恩彥吉大神教學
> [name=Marsgoat][time=Feb 16, 2023]
### Reference
[回到題目列表](https://hackmd.io/@Marsgoat/leetcode_every_day)