題目說明
給你一個字符串 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 只包含小寫英文字母。
解題思路一
- 首先要理解這個題。這道題明顯還是跟
前綴
有關係的東西,毫無疑問肯定又需要用到動態規劃
保存狀態。
- 我們先來看子字符串中的各元音字母的個數,題目呢,要求的是
偶數次
,那麼我們的a,e,i,o,u
的出現次數是不是可以轉化爲出現次數
的奇偶性
呢?即
nums % 2 === 1 是奇數次
nums % 2 === 0 是偶數次
- 那麼我們的
a,e,i,o,u
的各自狀態就只有兩種情況啦,0 or 1
,例如'a': 0,
'e': 0,
'o': 1,
'i': 0,
'u': 0
那我們現在用二進制來表示一下它:00100
, 那麼類似於這樣的表示有幾種情況呢? 2 x 2 x 2 x 2 x 2 = 32
僅僅只有32
種情況,就可以完全表示元音字母的所有狀態
了。那麼我們聲明一個長度爲32
的status
數組,值都初始化爲-1
。
- 按照正常思維,符合條件的情況有兩種子字符串,一是
從頭開始
的,一個是從中間開始
的。
- 從頭開始的很容易理解(假設首位下標爲
0,i
),只要從0到i
,狀態爲00000
就可以了。代表都是偶數次出現。
- 從中間開始的話(假設首位下標爲
j,i
),是從j到i
這個子字符串的狀態爲00000
- 那什麼情況下子字符串的狀態可以是00000呢? 那當然是i,j各自的狀態一致的時候,因爲同狀態互減纔會爲0,例如
01000 - 01000 = 00000
- so,01000在字串中是會出現多次的,因爲
2%2 == 0, 4%2===0
狀態也是會重複的,所以我們想求出這個狀態之間的最大間距,就要記錄該狀態最早出現
的下標。
- 好了,理解了這個狀態之後,我們明確了我們要記錄的值,記錄該位置的狀態下的最早下標。
- 我們是不是可以記錄一下,從
第1個字母開始到第i個字母之間
的各元音狀態
呢?
- 例如:
"eleetminicoworoep"
對應的狀態數組[01000,01000,00000,01000,01000,01000,01100...]
i 爲 0,1,3,4,5
的時候狀態都一致,那麼我們只需要記錄status[8] = 0,
取最小值即可。
- 所以
status
數組記錄的是32種狀態各自在字符串中最早出現的下標
- 最後我們遍歷字符串,求出每一位的
狀態key(例如01000)
,根據這個key和下標i
,我們去status
裏面找key
的最小下標status[key]
,然後用i - status[key]
求出距離長度。若status[key]爲-1
,則將下標i賦值給status[key] = i
代碼實現一
var findTheLongestSubstring = function(s) {
let hash = {
'a': 0,
'e': 0,
'o': 0,
'i': 0,
'u': 0
}
let status = new Array(32).fill(-1);
status[0] = 0;
let max = 0;
for (let i = 0; i < s.length; i++) {
let key = 0;
hash[s.charAt(i)] !== undefined ? hash[s.charAt(i)] = (hash[s.charAt(i)] + 1) % 2 : '';
key += hash['a'] + (hash['e'] << 1) + (hash['i'] << 2) + (hash['o'] << 3) + (hash['u'] << 4)
if (status[key] === -1) {
status[key] = i + 1;
} else {
max = Math.max(max, i + 1 - status[key])
}
}
return max;
};