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"))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章