1371. 每個元音包含偶數次的最長子字符串【前綴和 + 狀態壓縮】

原題地址:LeetCode 1371. 每個元音包含偶數次的最長子字符串

題目:

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

思路

前綴和:

1.對於長度爲 L 的字符串 S, 用一個輔助數組int flag[L][5]來記錄 ,flag[x][0…5] 表示從 S[0] 到 S[x] 這個子串中 aeiou的個數 。這步需要用 O(n)時間 。
2.此時 則有 子串 S[x1] 至 S[x2] 中 aeiou 的個數 是 (flag[x2][0…5] 減 flag[x1][0…5] )
3.每個子串的aeiou的個數 已知,則需要 O(n^2) 時間遍歷所有子串 , O(n^2)太大,還需要繼續優化。

狀態壓縮:


4.根據題意,只需要判斷次數是不是偶數,而不關心具體出現多少次 。所以 ,每個字母的狀態用0,1兩個狀態就可以表示奇偶。



5.進一步可以推出: 如果 子串 S[x1] 至 S[x2] 包含字母 aeiou 的次數是偶數,則 flag[x1][0…5] 與 flag[x2][0…5] 相等 。由於1個字母的狀態只需要1位二進制表示,5個字母只需要5位便可表示所有狀態。
所以輔助數組可以縮減爲char flag[L]。


6.還可以再優化,5個字母一共2^5 = 32中狀態 ,我們可以把定義一個 大小爲32的數組來記錄 每種狀態第一次出現的下標 ,例如: 假設 遍歷到 S[x] 時 第一次出現二進制狀態 (11111)(即所有字母都出現奇數次) 。 則 記 flag[31] = x (二進制11111 轉10進製爲 31) ,當 遍歷到 S[x2]時 遇到同樣狀態,則此時 S的子串 S[x] 至S[x1] 中各字母的個數都爲偶數
7.所以只需要遍歷一遍字符串即可得出結果 時間複雜度 O(n) ,空間複雜度爲常數 O(1)


代碼:

    int findTheLongestSubstring(string s) {
        int flag[32] ;
        for(int i = 0;i < 32; i++){
            flag[i] = -2;
        }
        flag[0] = -1;
        int curState = 0;
        int maxLen = 0;
        for(int i = 0 ; i < s.length() ; i ++){
            switch (s[i]){
                case 'a': curState ^= (1 << 0); break;
                case 'e': curState ^= (1 << 1); break;
                case 'i': curState ^= (1 << 2); break;
                case 'o': curState ^= (1 << 3); break;
                case 'u': curState ^= (1 << 4); break;
                default: break;
            }
            if(flag[curState] != -2){
                maxLen = maxLen > (i - flag[curState] ) ? maxLen : (i - flag[curState] );
            }
            else{
                flag[curState] = i;
            }
        }
        return maxLen;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章