1371、每个元音包含偶数次的最长子字符串

  1. 1371、每个元音包含偶数次数的最长字符子串
    1. 示例 1:
    2. 示例 2:
  2. 题解
    1. 1、暴力枚举
    2. 前缀和 + 状态压缩
    3. 3、动态规划

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、暴力枚举

遍历每一个子串,然后求出元音字母的个数,判断是否满足要求。

可惜超时了。

class Solution {
    char[] vowels = {'a','e','i','o','u'};
    public int findTheLongestSubstring(String s) {
        char[] chs = s.toCharArray();
        int res = 0;
        for (int i = 0;i < chs.length;i++) {
            for (int j = i;j < chs.length;j++) {
                if (res > j-i+1) {
                    continue;
                }
                int[] chars = new int[26];
                for (int k = i;k <= j;k++) {
                    chars[chs[k] - 'a']++;
                }
                boolean isVaild = true;
                for (int k = 0;k < vowels.length && isVaild;k++) {
                    if (chars[vowels[k]-'a'] % 2 == 1) {
                        isVaild = false;
                    }
                }
                if (isVaild) {
                    res = Math.max(res,j-i+1);
                }
            }
        }
        return res;
    }
}

前缀和 + 状态压缩

我们先来考虑暴力方法怎么做。最直观的方法无非就是枚举所有子串,遍历子串中的所有字符,统计元音字母出现的个数。如果符合条件,我们就更新答案,但这样肯定会因为超时而无法通过所有测试用例。

再回顾一下上面的操作,其实每个子串对应着一个区间,那么有什么方法可以在不重复遍历子串的前提下,快速求出这个区间里元音字母出现的次数呢?答案就是前缀和,对于一个区间,我们可以用两个前缀和的差值,得到其中某个字母的出现次数。
我们对每个元音字母维护一个前缀和,定义 $\textit{pre}[i][k]$] 表示在字符串前 i 个字符中,第 k 个元音字母一共出现的次数。假设我们需要求出 [l,r] 这个区间的子串是否满足条件,那么我们可以用 $pre[r][k]-pre[l-1][k]$,在 O(1) 的时间得到第 kk 个元音字母出现的次数。对于每一个元音字母,我们都判断一下其是否出现偶数次即可。

class Solution {
    public int findTheLongestSubstring(String s) {
        int n = s.length();
        // 存储状态对应的位置
        int[] pos = new int[1 << 5];
        Arrays.fill(pos, -1);
        int ans = 0, status = 0;
        pos[0] = 0;
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            // 跟新元音数组的奇偶状态
            if (ch == 'a') {
                status ^= (1 << 0);
            } else if (ch == 'e') {
                status ^= (1 << 1);
            } else if (ch == 'i') {
                status ^= (1 << 2);
            } else if (ch == 'o') {
                status ^= (1 << 3);
            } else if (ch == 'u') {
                status ^= (1 << 4);
            }
            if (pos[status] >= 0) {
                // 计算当前位置与同一状态的上一个位置的差
                ans = Math.max(ans, i + 1 - pos[status]);
            } else {
                // 存放状态第一次的位置
                pos[status] = i + 1;
            }
        }
        return ans;
    }
}

// 链接:https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/solution/mei-ge-yuan-yin-bao-han-ou-shu-ci-de-zui-chang-z-2/

3、动态规划

dp[i]作为以字符串i位作为结尾的、满足要求的最长子串长度

  • 如果i不是元音,那么dp[i][j]=dp[i-1][j]+1
  • 如果i是元音,那么找到它对应的位,假设是x,那么dp[i][j] = dp[i-1][j xor x]

作者:he-yun-fei
链接:https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/solution/javadong-tai-gui-hua-by-he-yun-fei/

class Solution {
    public int findTheLongestSubstring(String s) {
        char[] chars = new char[]{'a', 'e', 'i', 'o', 'u'};
        int[] nums = new int[]{16, 8, 4, 2, 1};
        int[][] dp = new int[s.length() + 1][32];
        for (int i = 0; i <= s.length(); i++) for (int j = 0; j < 32; j++) dp[i][j] = -1;
        dp[0][0] = 0;
        int result = Integer.MIN_VALUE;
        for (int i = 0; i < s.length(); i++) {
            boolean found = false;
            for (int j = 0; j < chars.length; j++)
                if (chars[j] == s.charAt(i)) {
                    found = true;
                    for (int k = 0; k < 32; k++) if (dp[i][k] != -1) dp[i + 1][k ^ nums[j]] = dp[i][k] + 1;
                    dp[i + 1][0] = Math.max(0, dp[i + 1][0]);
                }
            if (!found) {
                dp[i + 1][0] = dp[i][0] + 1;
                for (int k = 1; k < 32; k++) if (dp[i][k] != -1) dp[i + 1][k] = dp[i][k] + 1;
            }
            result = Math.max(result, dp[i + 1][0]);
        }
        return result;
    }
}

// 作者:he-yun-fei
// 链接:https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/solution/javadong-tai-gui-hua-by-he-yun-fei/

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:1371、每个元音包含偶数次的最长子字符串

Count:1.3k

Author:攀登

Created At:2020-07-26, 00:19:44

Updated At:2024-06-15, 15:52:32

Url:http://jiafeimao-gjf.github.io/2020/07/26/1371%E3%80%81%E6%AF%8F%E4%B8%AA%E5%85%83%E9%9F%B3%E5%8C%85%E5%90%AB%E5%81%B6%E6%95%B0%E6%AC%A1%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation