Skip to content

update #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ The data structures mainly include:
- [0516.longest-palindromic-subsequence](./problems/516.longest-palindromic-subsequence.md)
- [0518.coin-change-2](./problems/518.coin-change-2.md)
- [0547.friend-circles](./problems/547.friend-circles-en.md) 🆕✅
- [0560.subarray-sum-equals-k](./problems/560.subarray-sum-equals-k.en.md) ✅
- [0609.find-duplicate-file-in-system](./problems/609.find-duplicate-file-in-system.md)
- [0875.koko-eating-bananas](./problems/875.koko-eating-bananas.md)
- [0877.stone-game](./problems/877.stone-game.md)
Expand Down Expand Up @@ -246,14 +247,14 @@ The data structures mainly include:

### Summary of Data Structures and Algorithm

- [Data Structure](./thinkings/basic-data-structure-en.md) (Drafts)
- [Basic Algorithm](./thinkings/basic-algorithm-en.md)(Drafts)
- [Data Structure](./thinkings/basic-data-structure-en.md)
- [Basic Algorithm](./thinkings/basic-algorithm-en.md)
- [Binary Tree Traversal](./thinkings/binary-tree-traversal.en.md)✅
- [Dynamic Programming](./thinkings/dynamic-programming-en.md)
- [Huffman Encode and Run Length Encode](./thinkings/run-length-encode-and-huffman-encode-en.md)
- [Bloom Filter](./thinkings/bloom-filter-en.md)
- [String Problems](./thinkings/string-problems-en.md)
- [Sliding Window Technique](./thinkings/slide-window.en.md)
- [Dynamic Programming](./thinkings/dynamic-programming-en.md)
- [Huffman Encode and Run Length Encode](./thinkings/run-length-encode-and-huffman-encode-en.md)
- [Bloom Filter](./thinkings/bloom-filter-en.md)
- [String Problems](./thinkings/string-problems-en.md)
- [Sliding Window Technique](./thinkings/slide-window.en.md)

### Anki Flashcards

Expand Down
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
- [0322.coin-change](./problems/322.coin-change.md)
- [0328.odd-even-linked-list](./problems/328.odd-even-linked-list.md)
- [0334.increasing-triplet-subsequence](./problems/334.increasing-triplet-subsequence.md)
- [0343.integer-break](./problems/343.integer-break.md)🆕
- [0365.water-and-jug-problem](./problems/365.water-and-jug-problem.md)
- [0378.kth-smallest-element-in-a-sorted-matrix](./problems/378.kth-smallest-element-in-a-sorted-matrix.md)
- [0380.insert-delete-getrandom-o1](./problems/380.insert-delete-getrandom-o1.md)🆕
Expand All @@ -246,25 +247,26 @@ leetcode 题解,记录自己的 leetcode 解题之路。
- [0877.stone-game](./problems/877.stone-game.md)
- [0887.super-egg-drop](./problems/887.super-egg-drop.md)
- [0900.rle-iterator](./problems/900.rle-iterator.md)
- [0912.sort-an-array](./problems/912.sort-an-array.md) 🆕
- [0912.sort-an-array](./problems/912.sort-an-array.md)
- [0935.knight-dialer](./problems/935.knight-dialer.md) 🆕
- [1011.capacity-to-ship-packages-within-d-days](./problems/1011.capacity-to-ship-packages-within-d-days.md) 🆕
- [1011.capacity-to-ship-packages-within-d-days](./problems/1011.capacity-to-ship-packages-within-d-days.md)
- [1014.best-sightseeing-pair](./problems/1014.best-sightseeing-pair.md) 🆕
- [1015.smallest-integer-divisible-by-k](./problems/1015.smallest-integer-divisible-by-k.md) 🆕
- [1015.smallest-integer-divisible-by-k](./problems/1015.smallest-integer-divisible-by-k.md)
- [1019.next-greater-node-in-linked-list](./problems/1019.next-greater-node-in-linked-list.md) 🆕
- [1020.number-of-enclaves](./problems/1020.number-of-enclaves.md) 🆕
- [1023.camelcase-matching](./problems/1023.camelcase-matching.md) 🆕
- [1020.number-of-enclaves](./problems/1020.number-of-enclaves.md)
- [1023.camelcase-matching](./problems/1023.camelcase-matching.md)
- [1031.maximum-sum-of-two-non-overlapping-subarrays](./problems/1031.maximum-sum-of-two-non-overlapping-subarrays.md)
- [1104.path-in-zigzag-labelled-binary-tree](./problems/1104.path-in-zigzag-labelled-binary-tree.md) 🆕
- [1131.maximum-of-absolute-value-expression](./problems/1131.maximum-of-absolute-value-expression.md) 🆕
- [1104.path-in-zigzag-labelled-binary-tree](./problems/1104.path-in-zigzag-labelled-binary-tree.md)
- [1131.maximum-of-absolute-value-expression](./problems/1131.maximum-of-absolute-value-expression.md)
- [1186.maximum-subarray-sum-with-one-deletion](./problems/1186.maximum-subarray-sum-with-one-deletion.md) 🆕
- [1218.longest-arithmetic-subsequence-of-given-difference](./problems/1218.longest-arithmetic-subsequence-of-given-difference.md) 🆕
- [1227.airplane-seat-assignment-probability](./problems/1227.airplane-seat-assignment-probability.md) 🆕
- [1261.find-elements-in-a-contaminated-binary-tree](./problems/1261.find-elements-in-a-contaminated-binary-tree.md) 🆕
- [1262.greatest-sum-divisible-by-three](./problems/1262.greatest-sum-divisible-by-three.md) 🆕
- [1297.maximum-number-of-occurrences-of-a-substring](./problems/1297.maximum-number-of-occurrences-of-a-substring.md) 🆕
- [1218.longest-arithmetic-subsequence-of-given-difference](./problems/1218.longest-arithmetic-subsequence-of-given-difference.md)
- [1227.airplane-seat-assignment-probability](./problems/1227.airplane-seat-assignment-probability.md)
- [1261.find-elements-in-a-contaminated-binary-tree](./problems/1261.find-elements-in-a-contaminated-binary-tree.md)
- [1262.greatest-sum-divisible-by-three](./problems/1262.greatest-sum-divisible-by-three.md)
- [1297.maximum-number-of-occurrences-of-a-substring](./problems/1297.maximum-number-of-occurrences-of-a-substring.md)
- [1310.xor-queries-of-a-subarray](./problems/1310.xor-queries-of-a-subarray.md) 🆕
- [1334.find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance](./problems/1334.find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance.md) 🆕
- [1371.find-the-longest-substring-containing-vowels-in-even-counts](./problems/1371.find-the-longest-substring-containing-vowels-in-even-counts.md) 🆕

#### 困难难度

Expand Down Expand Up @@ -305,12 +307,13 @@ leetcode 题解,记录自己的 leetcode 解题之路。
- [《构造二叉树》专题](https://lucifer.ren/blog/2020/02/08/%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%93%E9%A2%98/)
- [《贪婪策略》专题](./thinkings/greedy.md)
- [《深度优先遍历》专题](./thinkings/DFS.md)
- [滑动窗口(思路 + 模板)](./thinkings/slide-window.md) 🆕
- [滑动窗口(思路 + 模板)](./thinkings/slide-window.md)
- [位运算](./thinkings/bit.md) 🆕
- [设计题](./thinkings/design.md) 🆕
- [小岛问题](./thinkings/island.md) 🆕
- [最大公约数](./thinkings/GCD.md) 🆕
- [并查集](./thinkings/union-find.md) 🆕
- [前缀和](./thinkings/prefix.md) 🆕

### anki 卡片

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# 题目地址(1371. 每个元音包含偶数次的最长子字符串)

https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/

## 题目描述

```
给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 'a','e','i','o','u' ,在子字符串中都恰好出现了偶数次。



示例 1:

输入:s = "eleetminicoworoep"
输出:13
解释:最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。
示例 2:

输入:s = "leetcodeisgreat"
输出:5
解释:最长子字符串是 "leetc" ,其中包含 2 个 e 。
示例 3:

输入:s = "bcbcbc"
输出:6
解释:这个示例中,字符串 "bcbcbc" 本身就是最长的,因为所有的元音 a,e,i,o,u 都出现了 0 次。


提示:

1 <= s.length <= 5 x 10^5
s 只包含小写英文字母。

```

## 暴力法 + 剪枝

### 思路

首先拿到这道题的时候,我想到第一反应是滑动窗口行不行。 但是很快这个想法就被我否定了,因为滑动窗口(这里是可变滑动窗口)我们需要扩张和收缩窗口大小,而这里不那么容易。因为题目要求的是奇偶性,而不是类似“元音出现最多的子串”等。

突然一下子没了思路。那就试试暴力法吧。暴力法的思路比较朴素和直观。 那就是`双层循环找到所有子串,然后对于每一个子串,统计元音个数,如果子串的元音个数都是偶数,则更新答案,最后返回最大的满足条件的子串长度即可`。

这里我用了一个小的 trick。枚举所有子串的时候,我是从最长的子串开始枚举的,这样我找到一个满足条件的直接返回就行了(early return),不必维护最大值。`这样不仅减少了代码量,还提高了效率。`

### 代码

代码支持:Python3

Python3 Code:

```python

class Solution:
def findTheLongestSubstring(self, s: str) -> int:
for i in range(len(s), 0, -1):
for j in range(len(s) - i + 1):
sub = s[j:j + i]
has_odd_vowel = False
for vowel in ['a', 'e', 'i', 'o', 'u']:
if sub.count(vowel) % 2 != 0:
has_odd_vowel = True
break
if not has_odd_vowel: return i
return 0

```

**复杂度分析**

- 时间复杂度:双层循环找出所有子串的复杂度是$O(n^2)$,统计元音个数复杂度也是$O(n)$,因此这种算法的时间复杂度为$O(n^3)$。
- 空间复杂度:$O(1)$

## 前缀和 + 剪枝

### 思路

上面思路中`对于每一个子串,统计元音个数`,我们仔细观察的话,会发现有很多重复的统计。那么优化这部分的内容就可以获得更好的效率。

对于这种连续的数字问题,这里我们考虑使用[前缀和](https://oi-wiki.org/basic/prefix-sum/)来优化。

经过这种空间换时间的策略之后,我们的时间复杂度会降低到$O(n ^ 2)$,但是相应空间复杂度会上升到$O(n)$,这种取舍在很多情况下是值得的。

### 代码

代码支持:Python3,Java

Python3 Code:

```python
class Solution:
i_mapper = {
"a": 0,
"e": 1,
"i": 2,
"o": 3,
"u": 4
}
def check(self, s, pre, l, r):
for i in range(5):
if s[l] in self.i_mapper and i == self.i_mapper[s[l]]: cnt = 1
else: cnt = 0
if (pre[r][i] - pre[l][i] + cnt) % 2 != 0: return False
return True
def findTheLongestSubstring(self, s: str) -> int:
n = len(s)

pre = [[0] * 5 for _ in range(n)]

# pre
for i in range(n):
for j in range(5):
if s[i] in self.i_mapper and self.i_mapper[s[i]] == j:
pre[i][j] = pre[i - 1][j] + 1
else:
pre[i][j] = pre[i - 1][j]
for i in range(n - 1, -1, -1):
for j in range(n - i):
if self.check(s, pre, j, i + j):
return i + 1
return 0
```

Java Code:

```java
class Solution {
public int findTheLongestSubstring(String s) {

int len = s.length();

if (len == 0)
return 0;

int[][] preSum = new int[len][5];
int start = getIndex(s.charAt(0));
if (start != -1)
preSum[0][start]++;

// preSum
for (int i = 1; i < len; i++) {

int idx = getIndex(s.charAt(i));

for (int j = 0; j < 5; j++) {

if (idx == j)
preSum[i][j] = preSum[i - 1][j] + 1;
else
preSum[i][j] = preSum[i - 1][j];
}
}

for (int i = len - 1; i >= 0; i--) {

for (int j = 0; j < len - i; j++) {
if (checkValid(preSum, s, i, i + j))
return i + 1
}
}
return 0
}


public boolean checkValid(int[][] preSum, String s, int left, int right) {

int idx = getIndex(s.charAt(left));

for (int i = 0; i < 5; i++)
if (((preSum[right][i] - preSum[left][i] + (idx == i ? 1 : 0)) & 1) == 1)
return false;

return true;
}
public int getIndex(char ch) {

if (ch == 'a')
return 0;
else if (ch == 'e')
return 1;
else if (ch == 'i')
return 2;
else if (ch == 'o')
return 3;
else if (ch == 'u')
return 4;
else
return -1;
}
}
```

**复杂度分析**

- 时间复杂度:$O(n^2)$。
- 空间复杂度:$O(n)$

## 前缀和 + 状态压缩

### 思路

前面的前缀和思路,我们通过空间(prefix)换取时间的方式降低了时间复杂度。但是时间复杂度仍然是平方,我们是否可以继续优化呢?

实际上由于我们只关心奇偶性,并不关心每一个元音字母具体出现的次数。因此我们可以使用`是奇数,是偶数`两个状态来表示,由于只有两个状态,我们考虑使用位运算。

我们使用 5 位的二进制来表示以 i 结尾的字符串中包含各个元音的奇偶性,其中 0 表示偶数,1 表示奇数,并且最低位表示 a,然后依次是 e,i,o,u。比如 `10110` 则表示的是包含偶数个 a 和 o,奇数个 e,i,u,我们用变量 `cur` 来表示。

为什么用 0 表示偶数?1 表示奇数?

回答这个问题,你需要继续往下看。

其实这个解法还用到了一个性质,这个性质是小学数学知识:

- 如果两个数字奇偶性相同,那么其相减一定是偶数。
- 如果两个数字奇偶性不同,那么其相减一定是奇数。

看到这里,我们再来看上面抛出的问题`为什么用 0 表示偶数?1 表示奇数?`。因为这里我们打算用异或运算,而异或的性质是:

如果对两个二进制做异或,会对其每一位进行位运算,如果相同则位 0,否则位 1。这和上面的性质非常相似。上面说`奇偶性相同则位偶数,否则为奇数`。因此很自然地`用 0 表示偶数?1 表示奇数`会更加方便。

### 代码

代码支持:Python3

Python3 Code:

```python

class Solution:
def findTheLongestSubstring(self, s: str) -> int:
mapper = {
"a": 1,
"e": 2,
"i": 4,
"o": 8,
"u": 16
}
seen = {0: -1}
res = cur = 0

for i in range(len(s)):
if s[i] in mapper:
cur ^= mapper.get(s[i])
# 全部奇偶性都相同,相减一定都是偶数
if cur in seen:
res = max(res, i - seen.get(cur))
else:
seen[cur] = i
return res

```

**复杂度分析**

- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$

## 关键点解析

- 前缀和
- 状态压缩

## 相关题目

- [掌握前缀表达式真的可以为所欲为!](https://lucifer.ren/blog/2020/01/09/1310.xor-queries-of-a-subarray/)
Loading