给你一个字符串 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 只包含小写英文字母。
解题思路
状态表示 将aeiou的出现次数组合表示为五位二进制数,当出现次数为偶数时 该位为0 奇数次则为1。
例如 aaeeioou 表示为 0 0 1 0 1
令f[r]表示下标从0~r的字符串的状态值
例如 alleiiou
f[0] = 1 0 0 0 0
f[1] = 1 0 0 0 0
f[2] = 1 0 0 0 0
f[3] = 1 1 0 0 0
f[4] = 1 1 1 0 0
f[5] = 1 1 0 0 0
f[6] = 1 1 0 1 0
f[7] = 1 1 0 1 1
由于奇数减奇数等于偶数,偶数减偶数等于偶数,所以当子串 [0,i] 与字串 [0,j] 对应字母的奇偶性相同时,新增的子串[i+1,j] 的状态一定是 00000,长度为j-i;因此可以记录每个状态第一次出现的位置,此后再出现该状态时相减即可,二者的差值则为中间新增子串的长度。
class Solution(object):
def findTheLongestSubstring(self, s):
"""
考的前缀和,bitmask与异或
将5个元音字母出现次数的奇偶视为一种状态,一共有32种状态使用二进制来表示。第 0 位为 1 表示 a 出现奇数次,
第一位为 1 表示 e 出现奇数次…… 以此类推。仅有状态 00000 符合题意。由于奇数减奇数等于偶数,偶数减偶数等于偶数,
所以当子串 [0,i] 与字串 [0,j] 对应字母的奇偶性相同时,新增的子串[i+1,j] 的状态一定是 00000,长度为j-i;
因此可以记录每个状态第一次出现的位置,此后再出现该状态时相减即可。
需要注意状态 0 首次出现的位置应该设定为 - 1。
"""
dp = [-2] * (1 << 5) # 初始时全部标记为-2,表示这个状态没出现过;五种元音的奇偶性组合一共有32种情况
dp[0] = -1 # 第一项初始化为-1,因为要考虑当第一项不是元音的情况;当第一项不是元音是长度就可以为1了
pattern = 0 # 记录前n个字符中元音字符的状态
res = 0 # 记录最长子串长度
for i in range(len(s)):
if s[i]=='a':
pattern ^= (1<<0)
elif s[i]=='e':
pattern ^= (1<<1)
elif s[i]=='i':
pattern ^= (1<<2)
elif s[i]=='o':
pattern ^= (1<<3)
elif s[i]=='u':
pattern ^= (1<<4)
# 等于-2表示这个状态第一次出现, 第一次出现时记录数组下标
if dp[pattern]==-2:
dp[pattern] = i
else:
# 如果不是第一次出现这个状态,说明以前出现过了,所以这两次出现之间的子串是满足条件的
res = max(res, i - dp[pattern])
return res