leetcode1371. 每个元音包含偶数次的最长子字符串/前缀和,哈希,状态压缩

给你一个字符串 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

基本思想

这道题除了暴力想法外,没有想到最终的解法,尽管其中的细节想到了

  • 暴力的解法是判断每一个子串中元音字母的个数是否满足条件,这样无疑时间复杂度是O(n2),这里外层循环每循环一次都要重复遍历字符
  • 为了不重复遍历字符,采用前缀和的思想,统计以当前字符结束的前i + 1个字符中元音字符的个数,那么当前子串中元音字符的个数就可以用做差的形式实现,但这样做时间复杂度依然是O(n2
  • 考虑到我们并不用关注子串中元音字母有多少个,只关注子串中原因字母的奇偶性就可以,那么我们在前缀和中保存前i + 1个字符中每一个元音字母的个数是奇数个(用1)表示,还是偶数个(用0)表示
  • 在判断的过程中,要保证子串的长度最长,我们只需找到和当前状态一样的最早出现的位置就可以求出以当前字符结束的最长子串,因为奇数–奇数=偶数,偶数–偶数=偶数
  • 为了便于找到第一次出现该状态的位置,我们只能用哈希
  • 对于元音出现的次数的奇偶性对应的状态,便于处理,采用状态压缩,用二进制来表示(1表示出现的次数为奇数,0表示出现的次数为偶数),(11011) = 27,(00000)---- (11111)共32种状态
class Solution {
public:
    int findTheLongestSubstring(string s) {
        vector<int> status(32, -1);//记录状态最早出现的位置
        int mlen = 0, cur = 0;
        status[0] = 0;//当一个字符都没有时,状态为0
        for(int i = 0; i < s.length(); ++i){
            if(s[i] == 'a')
                cur ^= 1 << 0;
            else if(s[i] == 'e')
                cur ^= 1 << 1;
            else if(s[i] == 'i')
                cur ^= 1 << 2;
            else if(s[i] == 'o')
                cur ^= 1 << 3;
            else if(s[i] == 'u')
                cur ^= 1 << 4;
            if(status[cur] != -1){
                mlen = max(mlen, i + 1 - status[cur]);
            }
            else
                status[cur] = i + 1; 
        }
        return mlen;
    }
};

注意上述代码中一定要注意:状态数组下标为0,也就是状态码为0的情况下,对应的是初始状态,一个字符都没有的时候。该数组中记录的是字符串下标+1,可以理解为前i个字符的状态码为其状态数组的下标。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页