1371. Find the Longest Substring Containing Vowels in Even Counts
Title
給你一個(gè)字符串 s ,請(qǐng)你返回滿足以下條件的最長子字符串的長度:每個(gè)元音字母,即 ‘a(chǎn)’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出現(xiàn)了偶數(shù)次。
示例 1:
輸入:s = “eleetminicoworoep”
輸出:13
解釋:最長子字符串是 “l(fā)eetminicowor” ,它包含 e,i,o 各 2 個(gè),以及 0 個(gè) a,u 。
示例 2:
輸入:s = “l(fā)eetcodeisgreat”
輸出:5
解釋:最長子字符串是 “l(fā)eetc” ,其中包含 2 個(gè) e 。
示例 3:
輸入:s = “bcbcbc”
輸出:6
解釋:這個(gè)示例中,字符串 “bcbcbc” 本身就是最長的,因?yàn)樗械脑?a,e,i,o,u 都出現(xiàn)了 0 次。
Solve
前綴和+狀態(tài)壓縮
先來考慮最容易想到的暴力方法:枚舉所有子串,遍歷子串中的所有字符,統(tǒng)計(jì)元音字母出現(xiàn)的個(gè)數(shù),如果符合條件就更新答案,不用想都知道這樣肯定超時(shí)。
其實(shí)每個(gè)子串對(duì)應(yīng)著一個(gè)區(qū)間,那么有什么方法可以在不重復(fù)遍歷子串的前提下,快速求出這個(gè)區(qū)間里元音字母出現(xiàn)的次數(shù)呢?答案就是前綴和,對(duì)于一個(gè)區(qū)間,可以用兩個(gè)前綴和的差值,得到其中某個(gè)字母出現(xiàn)次數(shù)。
對(duì)每個(gè)元音字母維護(hù)一個(gè)前綴和,定義pre[i][k]表示在字符串前i個(gè)字符中,第k個(gè)元音字母一共出現(xiàn)的次數(shù)。
假設(shè)我們需要求出[l,r]這個(gè)區(qū)間的子串是否滿足條件,那么我們可以用pre[r][k]-pre[l-1][k],在O(1)的時(shí)間得到第k個(gè)元音字母出現(xiàn)的次數(shù),對(duì)于每個(gè)元音字母都判斷一下其是否出現(xiàn)偶數(shù)次即可。
利用前綴和優(yōu)化了統(tǒng)計(jì)子串的時(shí)間復(fù)雜度,然而枚舉所有子串的復(fù)雜度仍需要O(n2),還是不足以通過本題,還需要繼續(xù)進(jìn)行優(yōu)化,避免枚舉所有子串。
考慮枚舉字符串的每個(gè)位置i,計(jì)算以它結(jié)尾的滿足條件的最長字符串長度。其實(shí)我們要做的就是快速找到最小的j∈[0,i),滿足pre[i][k]?pre[j][k]均為偶數(shù),那么以i結(jié)尾的最長字符串s[j+1,i]長度就是i-j。
但是單單利用前綴和,還是無法找到i和j相關(guān)的恒等式。這道題還有一個(gè)性質(zhì)沒有充分利用:目標(biāo)子串中,每個(gè)元音字母都恰好出現(xiàn)了偶數(shù)次。
偶數(shù)這個(gè)條件其實(shí)告訴了我們,對(duì)于滿足條件的子串,兩個(gè)前綴和pre[i][k]和pre[j][k]的奇偶性一定是相同的,因此我們可以對(duì)前綴和稍作修改,從維護(hù)元音字母出現(xiàn)的次數(shù)改作維護(hù)元音字母出現(xiàn)次數(shù)的奇偶性。
這樣我們只要實(shí)時(shí)維護(hù)每個(gè)元音字母出現(xiàn)的奇偶性,那么s[j+1,i]滿足條件當(dāng)且僅當(dāng)對(duì)于所有的k,pre[i][k]和pre[j][k]的奇偶性都相等,此時(shí)我們就可以利用哈希表存儲(chǔ)每一種奇偶性對(duì)應(yīng)最早出現(xiàn)的位置,邊遍歷邊更新答案。
其實(shí)到這里就差不多了,但是還可以進(jìn)一步優(yōu)化編碼方式,如果直接以每個(gè)元音字母出現(xiàn)次數(shù)的奇偶性為哈希表中的鍵難免有些冗余,因此可以額外定義一個(gè)狀態(tài):
{a: cnta, // a 出現(xiàn)次數(shù)的奇偶性e: cnte, // e 出現(xiàn)次數(shù)的奇偶性i: cnti, // i 出現(xiàn)次數(shù)的奇偶性o: cnto, // o 出現(xiàn)次數(shù)的奇偶性u(píng): cntu // u 出現(xiàn)次數(shù)的奇偶性 }將這么一個(gè)結(jié)構(gòu)當(dāng)做哈希表存儲(chǔ)的鍵值,如果題目稍作修改擴(kuò)大了字符集,那么維護(hù)起來可能會(huì)比較吃力??紤]到出現(xiàn)次數(shù)的奇偶性無非就兩個(gè)值,0代表出現(xiàn)了偶數(shù)次,1代表出現(xiàn)了奇數(shù)次,我們可以將其壓縮到一個(gè)二進(jìn)制數(shù)中,第k位的0或1代表了k個(gè)元音字母出現(xiàn)的奇偶性,這樣我們也不需要使用哈希表,直接用一個(gè)長度為32的數(shù)組來存儲(chǔ)對(duì)應(yīng)狀態(tài)出現(xiàn)的最大位置即可。
為什么用 0 表示偶數(shù)?1 表示奇數(shù)?
因?yàn)檫@里我們打算用異或運(yùn)算,而異或的性質(zhì)是:如果對(duì)兩個(gè)二進(jìn)制做異或,會(huì)對(duì)其每一位進(jìn)行位運(yùn)算,如果相同則為 0,否則為 1。這和上面的性質(zhì)非常相似。上面說奇偶性相同則位偶數(shù),否則為奇數(shù)。因此很自然地用 0 表示偶數(shù),1 表示奇數(shù)會(huì)更加方便。
復(fù)雜度分析
時(shí)間復(fù)雜度:O(n)
其中 n 為字符串 s 的長度。我們只需要遍歷一遍字符串即可求得答案,因此時(shí)間復(fù)雜度為 O(n)。
空間復(fù)雜度:O(S)
其中 S 表示元音字母壓縮成一個(gè)狀態(tài)數(shù)的最大值,在本題中 S = 32。我們需要對(duì)應(yīng) S 大小的空間來存放每個(gè)狀態(tài)第一次出現(xiàn)的位置,因此需要 O(S) 的空間復(fù)雜度。
Code
def findTheLongestSubstring(self, s: str) -> int:mapper = {'a': 1, 'e': 2, 'i': 4, 'o': 8, 'u': 16}seen = {0: -1}res = cur = 0for i in range(len(s)):if s[i] in mapper:cur ^= mapper.get(s[i])if cur in seen:res = max(res, i - seen.get(cur))else:seen[cur] = ireturn res總結(jié)
以上是生活随笔為你收集整理的1371. Find the Longest Substring Containing Vowels in Even Counts的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 680. Valid Palindrom
- 下一篇: 76. 最小覆盖子串