题目来源:题目
题目
给你一个字符串 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 只包含小写英文字母。
思路
思路一暴力算法
找到所有的字串,判断该字串中的所有元音字母的个数是否为偶数,如果满足都是偶数的条件,则更新字串最大长度。
其中,找到所有字串的时间复杂度为O(n^2),在字串中找元音字母个数的时间复杂度为O(n),所以总的时间复杂度为O(n^3).会超过时间限制。
思路二前缀和
考虑给定一个字串,如何更快的判断该字串中元音字母的个数。假设给定字串为[i,j],表示原字符串的第i个字母到第j个字母。可以用前缀和的方式记录从0-i和0-j个字符中每个元音的个数pre[i][k],pre[j][k],则字串[i,j]中元音个数为pre[j][k] – pre[i-1][k]。k表示五个元音字符的第k个。
由于我们只要判断该字串中元音字母的个数的奇偶,所以我们可以用前缀和记录元音个数的奇偶。由于奇数减去奇数和偶数减去偶数都为偶数,所有只要pre[j][k]和pre[i-1][k]的奇偶性相同即可。
现在考虑如何找字串?不用遍历所有的字串,而是考虑直接求出以下标j结尾的最大字串长度,就可以先找到与第j个字母的奇偶性相同的字符下标i,然后用j-i+1就是以j结尾的字串最大长度。所以我们可以记录每种奇偶性第一次出现的位置。
因为有5个元音字母,所以奇偶性一共有32中,可以用5位记录奇偶性,0表示该字母出现偶次,1表示该字母出现奇数次。
代码
public static int findTheLongestSubstring(String s) {
int[] pos = new int[32];
int ans = 0, status = 0;
Arrays.fill(pos, -1);
pos[0] = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == 'a') {
status ^= (1 << 0);
} else if (c == 'o') {
status ^= (1 << 1);
} else if (c == 'e'){
status ^= (1 << 2);
} else if (c == 'i') {
status ^= (1 << 3);
} else if (c == 'u') {
status ^= (1 << 4);
}
if (pos[status] >= 0) {
ans = Math.max(ans, i + 1 - pos[status]);
} else {
pos[status] = i + 1;
}
}
return ans;
}
总结
思路二的时间复杂度为O(n).