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 只包含小写英文字母。

题解

一开始最容易想到的就是暴力枚举法,我用暴力枚举写了一下,结果显然是超时。

之后看了官方题解和一些大神们的解答,发现这道题可以用状态压缩+前缀和+哈希表来解决。

前缀和

前缀是指[0, x](从0开始的子串),用前缀和来表示子串区间[i, j],可以将两个变量化简为一个变量:

[i, j] = [0, j] - [0, i - 1]

[0, j][0, i - 1] 都可以用[0, x]表示。

现在我们关心的是子串中元音字母出现的奇偶情况,而不是元音字母出现的次数,因此我们用状态压缩来表示每个元音字母出现的奇偶情况。

状态压缩

遇到有限的参数(小于20个)表状态, 想到状态压缩。

这里是将元音字母的出现次数做状态压缩,元音字母出现偶数次记为0,出现奇数次记为1。然后用二进制表示每个字母出现的奇偶次。比如在asaus这个字符串中,a出现2次(偶数),u出现1次(奇数),其他元音字母都出现0次(偶数),那么它对应的状态就是10000(uoiea),十进制数就是16。

因此,设置一个变量pattern表示当前的状态,pattern的取值范围就是0~31(00000~11111)

那如何计算pattern呢?首先设置一个hash表,将元音字母用对应的二进制表示。这里将二进制转化为了十进制。直接用1<<n也是可以的。

const letters = { 'a': 1, 'e': 2, 'i': 4, 'o': 8, 'u': 16 };

根据异或的特点(相同为0,不同为1),一个字母如果出现偶数次,异或的结果一定为0。因此当元音字母出现时,就将原pattern与当前元音字母对应的二进制进行异或,并更新pattern。

pattern ^= letters[ch];     // ch为当前遍历到的元音字母

整体思路

  1. 变量声明
    • letters表:存放元音字母与对应的二进制数的对应关系
    • pattern:当前前缀区间的奇偶状态
    • hashmap:表示pattern与当前索引index的对应关系,初始放入{0: -1}
  2. 遍历字符串,如果当前字母为元音字母,更新pattern,在hashmap中存入pattern第一次出现的索引
  3. 计算当前位置与第一次索引之间的距离,如果比res大,更新res
  4. 返回res

代码

/**
 * 1371. 每个元音包含偶数次的最长子字符串
 * 给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:
 * 每个元音字母,即 'a','e','i','o','u' ,在子字符串中都恰好出现了偶数次。
 * 
 * 状态压缩 + 哈希表 + 前缀和
 * @param {string} s
 * @return {number}
 */
var findTheLongestSubstring = function (s) {
    let res = 0;    // 最长满足条件的子字符长度
    const letters = { 'a': 1, 'e': 2, 'i': 4, 'o': 8, 'u': 16 };
    let pattern = 0;    // pattern从0(00000)到31(11111),每一位对应aeiou,出现奇数次为1,偶数次为0
    let hashmap = {0: -1};      // {pattern: index}
    // 遍历s串
    for(let i = 0; i < s.length; i++) {
        let ch = s.charAt(i);   // 当前字符
        if (letters[ch] !== undefined) {
            // 当前字符为元音字母,异或更新pattern
            pattern ^= letters[ch];
            if(hashmap[pattern] === undefined) {
                // 记录状态pattern第一次出现的位置
                hashmap[pattern] = i;
            }
        }
        let dis = i - hashmap[pattern];
        res = Math.max(res, dis);
    }
    return res;
};
console.log(findTheLongestSubstring("eleetminicoworoep"))
// console.log(findTheLongestSubstring("oumzgd"))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章