1371. 每个元音包含偶数次的最长子字符串 前缀和,bitmask与异或

给你一个字符串 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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值