LeetCode每日一题1372每个元音包含偶数次的最长子字符串

昨天因有事学习中断,应该批评。后续要补上一题。
今天的题虽然官方给的标签是字符串,但实际上综合性比较强,包括了字符串、位运算、前缀和、哈希表等内容。话不多说,一起来捋一捋思路。
1371.给你一个字符串 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 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

暴力解法相信大家都能想到,利用循环遍历字符串来统计每一位结尾的子串中符合条件的长度,更新最大值。耗时一想就知道,通过必然没有戏,而面试时遇到这类题,暴力解也必然无法让面试官满意。
我们想一想暴力求解的操作中,对每一位结尾的字符串进行内层遍历时,其实是与之前有重复操作的,这与数组中的子部求和是类似的,优化重复操作,在后续中,直接取值。前缀和思想是解决这一问题的好办法,我们对第i位字符结尾的子串,统计其中各元音字符出现的个数,以数组pre[i][k]进行统计,k=0~4(共5种)。为避免重复操作,对以i结尾的子串找到其中满足条件的最大子串,即寻找以j为结尾的前缀和,使得pre[i][k]-pre[j][k]为偶数的最小j,长度即i-j。但由于有5个元音字母需要统计,对于不同的k值,差值不同。因此,我们需要对所有k都满足时进行最大值的更新。根据题目条件,所有元音字母都出现偶数次,即对于任意k值,pre[i][k]和pre[j][k]都是偶数,即若所有元音字母的前缀和之和为偶数,就满足题目的条件,即子串中,所有元音字母出现的奇偶性相同,均为偶数个,就满足。
转变思路,前缀和部分统计以i结尾的各元音字符的奇偶性。官方给出的位运算方法实在是妙,菜鸡流下来没技术的眼泪,再次觉得自己是个傻子。共5个元音字母,用0,1表示奇偶性,将五个状态都压缩到了一个二进数中,范围为(00000)-(11111),当五个元音字母均为偶数时,值为11111即31。你说妙不妙,C/C++位运算大法好呀,不仅速度快,节约空间,还妙用无穷,可惜我不会哈哈哈。

综上,编码的总体思路为:设置res记录最大子串长度,设置flag存储各位状态,初始为0即00000,对字符串按位遍历,判断字符,更新flag对应位。因为只有flag的i,j位置,满足要求,因此记录每一个flag第一次出现的位置,注意flag=0出现在空串时,即遍历前。当flag已经出现过,更新res,否则记录flag的位置。

class Solution {
public:
    int findTheLongestSubstring(string s) {
        if(s.empty())
            return 0;

        int res=0,size=s.size(),flag=0;//0表示出现偶数次
        vector<int> book(1<<5,-1);
        book[0]=0;
        for(int i=0;i<size;i++){
            if(s[i]=='a')
                flag^=1<<4;
            else if(s[i]=='e')
                flag^=1<<3;
            else if(s[i]=='i')
                flag^=1<<2;
            else if(s[i]=='o')
                flag^=1<<1;
            else if(s[i]=='u')
                flag^=1;
            
            if(~book[flag]){
                res=max(res,i+1-book[flag]);
            }else{
                book[flag]=i+1;
            }
        }
        return res;
    }
};

你可能感兴趣的:(算法学习,字符串,leetcode,算法)