题目
给你一个字符串 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 只包含小写英文字母。
解题思路
如果当前字符串符合要求,则返回当前字符串长度。否则对去掉首字母、去掉末尾字幕的2个子字符串递归。时间复杂度是 o ( n 2 ) o(n^2) o(n2),这样最糟糕的情况下有 o ( 1 0 11 ) o(10^{11}) o(1011)种情况,超时了
官方题解看不懂。。。放弃了。。。。
又看了一个top的题解,想明白了!
如果子串[0, i]和子串[0, j]的元音字母计数之差是偶数,则[i+1, j]是一个符合要求的子串。所以可以对每个位置index的子串[0, index],维护一个元音字母的字典,字典的键是一个字符串,用来表示aeiou出现的次数(如aeiou12345
表示a出现1次,e出现2次,i出现3次,o出现4次,u出现5次,字典的值是当前的index,如果键有变化则新增键值对,如果没变化则index ++。每次计数后遍历字典的键,找出符合键键相减后值均为偶数的值,然后再从值里面找差距最大的值即可。
然后改进,发现字典的键用字符串拼接完全多余。。。因为最终需要找的键键之差,是和奇偶性相关,所以考虑用异或来表示值。如果当前字母属于元音字母,则异或字母的ascii值,如果这个ascii值之前出现过,则为上述找到键键相减为偶数的index,此时相减即可
再再改进,可以用bit位来代表a,e,i,o,u而不是用ascii码来异或,这样计算速度能更快
代码
超时递归版:
class Solution:
def findTheLongestSubstring(self, s: str) -> int:
vow_cnt = set()
for each_char in s:
if each_char in ['a', 'e', 'i', 'o', 'u']:
if each_char in vow_cnt:
vow_cnt.remove(each_char)
else:
vow_cnt.add(each_char)
else:
continue
if not vow_cnt:
return len(s)
return max(self.findTheLongestSubstring(s[1:]), self.findTheLongestSubstring(s[:-1]))
改进1版:
lass Solution:
def findTheLongestSubstring(self, s: str) -> int:
stat_dict = {0: -1}
cur_stat = 0
ret_length = 0
for index, each_char in enumerate(s):
if each_char in ['a', 'e', 'i', 'o', 'u']:
cur_stat ^= ord(each_char)
if cur_stat in stat_dict:
ret_length = max(ret_length, index - stat_dict[cur_stat])
else:
stat_dict[cur_stat] = index
return ret_length