[LeetCode] 1371. Find the Longest Substring Containing Vowels in Even Counts

Given the string s, return the size of the longest substring containing each vowel an even number of times. That is, ‘a’, ‘e’, ‘i’, ‘o’, and ‘u’ must appear an even number of times.

Example 1:
Input: s = “eleetminicoworoep”
Output: 13
Explanation: The longest substring is “leetminicowor” which contains two each of the vowels: e, i and o and zero of the vowels: a and u.

Example 2:
Input: s = “leetcodeisgreat”
Output: 5
Explanation: The longest substring is “leetc” which contains two e’s.

Example 3:
Input: s = “bcbcbc”
Output: 6
Explanation: In this case, the given string “bcbcbc” is the longest because all vowels: a, e, i, o and u appear zero times.

Constraints:
1 <= s.length <= 5 x 10^5
s contains only lowercase English letters.

每个元音包含偶数次的最长子字符串。

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

思路

题意是给一个字符串 s,请你返回其中包含元音字母次数为偶数次的最长的子串。题意还算比较直观,注意最后得到的子串里面,每个出现的元音字母的出现次数都必须各自为偶数,同时这里的偶数次也可以是 0 次。

这个题因为 test case 的数据量很大的关系所以一般的暴力解是过不了的。先说一下暴力解的思路吧,创建一个 hashmap 记录五个元音字母的出现次数,用追击型的 two pointer 去扫描 input 字符串,然后看子串里面每个出现的元音字母的出现次数是否都各自为偶数。

这个题的最优解是有点类似 560 题前缀和的思路,统计在两个指针 [start, end] 之间出现的元音字母的出现次数。这里有一个数学定理需要记住,就是奇数 - 奇数 = 偶数,偶数 - 偶数 = 偶数。所以在按照前缀和的思路找字母出现次数的时候只要在 [start, end] 之间出现的次数为偶数即可。

如何知道出现次数为偶数呢?这里会用到位运算。因为只有五个元音字母,所以可以创建一个 5 位数的 bit mask,分别代表 aeiou。比如当遇到a的时候,就只把1左移一位(从右往左看),遇到e就把1左移两位,遇到i就把1左移三位,遇到o就把1左移三位,遇到u就把1左移四位。

遍历 input 的时候,当遇到某一个元音字母的时候,就把 1 左移若干位,然后跟 cur 做异或操作 XOR。因为 XOR 的操作是两数相同结果为 0,所以某一个字母遇到两次,则这一位上的数字为 0。跑一个例子吧,

1
2
3
4
5
6
7
8
9
10
11
input: abcadf

cur = 0 (00000),把这个mask存入hashmap,表示第一次出现 00000 这个mask的地方在下标为 -1 的地方

一开始遍历到a, bit mask = 00001, cur ^ 00001 = 00001,此时用hashmap记录一下<00001, i>,意思是第一次出现 00001 这个mask的地方在下标为 i 的地方

遍历到b, c的时候,因为不是元音字母所以直接跳过

当遇到第二个a的时候,cur ^ 00001 = 00001 ^ 00001 = 00000,mask又变回 00000 ,此时因为这个mask在hashmap里面已经有了,所以可以结算一下长度。

照着这个思路,可以计算出所有不同mask形成的最长的子串,输出最长的那个即可

复杂度

时间O(n)
空间O(n) - hashmap

代码

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int findTheLongestSubstring(String s) {
int res = 0;
int cur = 0;
int n = s.length();
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if (ch == 'a') {
cur ^= (1 << 1);
} else if (ch == 'e') {
cur ^= (1 << 2);
} else if (ch == 'i') {
cur ^= (1 << 3);
} else if (ch == 'o') {
cur ^= (1 << 4);
} else if (ch == 'u') {
cur ^= (1 << 5);
}
map.putIfAbsent(cur, i);
res = Math.max(res, i - map.get(cur));
}
return res;
}
}
// aeiou
// 11111
// 00000

[LeetCode] 1371. Find the Longest Substring Containing Vowels in Even Counts
https://shurui91.github.io/posts/1395124195.html
Author
Aaron Liu
Posted on
May 20, 2020
Licensed under