【字符串】B044_LC_每个元音包含偶数次的最长子字符串(状态压缩)

本文介绍了一种利用位运算解决寻找字符串中最长包含每个元音字母偶数次子串的问题的方法。通过使用5位二进制表示元音字母的出现次数,巧妙运用异或操作更新状态,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Problem

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.

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.

二、Solution

方法一:位运算

一开始用双指针做的,做着做着就做不下去了,看了一眼别人的思路,居然发现是位运算…

  • 用 5 个二进制位表示元音字母出现次数的种类:
    • 位 0 表示元音字母出现偶数次
    • 位 1 表示元音字母出现奇数次

为什么这样做?可以很方便地用异或来表示元音字母的频次的奇偶性,比如:

00001 ^ 00001 = 00000,前一个状态表示只有'a'出现奇数次,现在又出现一次'a',两个状态异或后状态为 00000
00001 ^ 00010 = 00011,上个状态是 00001,则表示a和e分别出现奇数次了

Q:状态表示有了,如果记录遍历过程中的状态呢?
A:5 个二进制位一共有 25=322^5 = 3225=32 种状态可以表示,数组 开 int[32] 即可

算法
  • 定义状态
    • pos[st]pos[st]pos[st] 的值 iii 表示子串 s[0:i] 的元音字母频次状态为 ststst 时在 iii 位置
  • 思考初始化:
    • pos[0]=0pos[0] = 0pos[0]=0 表示元音频次状态 0 在位置 0
  • 思考状态转移方程
    • 如果状态 st 没有出现过,即 pos[st]=−1pos[st] = -1pos[st]=1,则 pos[st]=i+1pos[st] = i+1pos[st]=i+1,记录一下当前位置,供下一次使用。
    • 否则该状态必定经过 >= 2 次,此时可以推断出当前状态再次出现,什么情况下状态才会再次出现?肯定是又遇到了遇到过的元音字母啦,故此时可记录临时答案与 max 中:max=i+1−pos[st]max = i+1-pos[st]max=i+1pos[st]
class Solution {
    public int findTheLongestSubstring(String S) {
		char s[] = S.toCharArray(), v[] = new char[] {'a', 'e', 'i', 'o', 'u'};
		int max = 0, st = 0, pos[] = new int[1<<5];
		Arrays.fill(pos, -1);
        pos[0] = 0;
        
		for (int i = 0; i < s.length; i++) {
            for (int k = 0; k < v.length; k++)
            if (s[i] == v[k]) {
                st ^= 1 << k;
                break;
            }
            if (pos[st] == -1)  pos[st] = i + 1;
            else 				max = Math.max(max, i + 1 - pos[st]);
        }
		return max;
    }
}

复杂度分析

  • 时间复杂度:O(n)O(n)O(n)
  • 空间复杂度:O(1)O(1)O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值