🔗 🔴 3445. Maximum Difference Between Even and Odd Frequency II

Problem Statement

題目簡述

給定一個僅由數字 '0''4' 組成的字串 ss 以及一個整數 kk

我們需要尋找一個長度至少為 kk 的子字串,使得其中某兩個不同字元 aabb 的出現次數差值 freq[a]freq[b]\text{freq}[a] - \text{freq}[b] 最大,且滿足:

  • 字元 aa 在該子字串中出現奇數次
  • 字元 bb 在該子字串中出現偶數次

傳回這個最大的差值。

註:子字串中可以包含除了 aabb 以外的其他字元。

Constraints

約束條件

  • 3s.length3×1043 \le s.\text{length} \le 3 \times 10^4
  • ss 僅由字元 '0''4' 組成。
  • 輸入保證至少存在一個合法的子字串,其中包含一個出現奇數次的字元與一個出現偶數次的字元。
  • 1ks.length1 \le k \le s.\text{length}

思路:枚舉雙字元 + 前綴和 + 雙指標滑動窗口

本題是一道非常經典的 「枚舉右,維護左」「前綴和轉化」 的綜合優化題。

1. 關鍵觀察:字元集極小

字串只包含 '0''4',字元集大小 Σ=5|\Sigma| = 5。因此可以直接枚舉有序字元對 (a,b)(a,b)

  • aa 代表在子字串中出現奇數次的字元。
  • bb 代表在子字串中出現偶數次的字元。
  • 一共只有 5×4=205 \times 4 = 20 種配對。

所以問題變成:對每一組 (a,b)(a,b),用 O(n)\mathcal{O}(n) 求出合法子字串的最大差值。


2. 前綴和與公式變形

固定 aabb 後,目標是在一個長度至少為 kk 的區間 [l,r][l,r] 中,最大化 aabb 的出現次數差。

定義前綴和陣列 sas_a,其中 sa[i]s_a[i] 表示前綴 s[0i1]s[0 \dots i-1] 中字元 aa 的出現次數。那麼:

suma(l,r)=sa[r+1]sa[l]sum_a(l, r) = s_a[r+1] - s_a[l]

同理,bb 的出現次數為 sumb(l,r)=sb[r+1]sb[l]sum_b(l, r) = s_b[r+1] - s_b[l]

我們希望最大化:

suma(l,r)sumb(l,r)=(sa[r+1]sa[l])(sb[r+1]sb[l])=(sa[r+1]sb[r+1])(sa[l]sb[l])\begin{aligned} sum_a(l, r) - sum_b(l, r) &= \big(s_a[r+1] - s_a[l]\big) - \big(s_b[r+1] - s_b[l]\big) \\ &= \big(s_a[r+1] - s_b[r+1]\big) - \big(s_a[l] - s_b[l]\big) \end{aligned}

式子右半部只跟左端點有關。因此枚舉右端點 rr(令 i=r+1i=r+1)時,只要維護所有合法左端點 ll(令 j=lj=l)中的最小 sa[l]sb[l]s_a[l]-s_b[l],就能更新答案。


3. 維護合法左端點

接下來的問題是:枚舉右端點 ii 時,哪些左端點 jj 可以拿來更新答案?

左端點需要滿足兩類「可加入」條件:

  • 長度足夠ijki-j \ge k
  • 區間內確實包含 aabbsa[j]<sa[i]s_a[j] < s_a[i]sb[j]<sb[i]s_b[j] < s_b[i]

因此可以用左指標 left 往右掃,把已經滿足上述條件的左端點加入維護結構。加入時,根據 sa[left]s_a[\text{left}]sb[left]s_b[\text{left}] 的奇偶性,把 sa[left]sb[left]s_a[\text{left}]-s_b[\text{left}] 更新到對應狀態的最小值中。

奇偶狀態的二進位表示

左端點的 sa[j]s_a[j]sb[j]s_b[j] 各有兩種奇偶性,可以壓成 44 種狀態:

state=((sa[j]mod2)1)(sb[j]mod2)\text{state} = ((s_a[j] \bmod 2) \ll 1) \mid (s_b[j] \bmod 2)

用大小為 44 的陣列 min_sl 記錄每種狀態下 sa[j]sb[j]s_a[j]-s_b[j] 的最小值。

加入合法左端點後,還要根據當前右端點的奇偶性查詢正確狀態:

  • aa 的出現次數 sa[i]sa[j]s_a[i]-s_a[j] 必須為奇數,所以 sa[j]s_a[j]sa[i]s_a[i] 奇偶性相反。
  • bb 的出現次數 sb[i]sb[j]s_b[i]-s_b[j] 必須為偶數,所以 sb[j]s_b[j]sb[i]s_b[i] 奇偶性相同。

也就是查詢:

  • sa[j]sa[i]1(mod2)s_a[j] \equiv s_a[i] \oplus 1 \pmod 2
  • sb[j]sb[i](mod2)s_b[j] \equiv s_b[i] \pmod 2

min_sl 取出這個狀態的最小值,即可更新答案。

為什麼加入左端點時要檢查 sa[j]<sa[i]s_a[j] < s_a[i]sb[j]<sb[i]s_b[j] < s_b[i]

只看長度會把「某個字元出現 00 次」的區間也算進來。雖然 00 是偶數,但題目要求選出的 aabb 都要在子字串中出現。

複雜度分析

  • 時間複雜度:O(Σ2n)\mathcal{O}(|\Sigma|^2 \cdot n)。其中 Σ=5|\Sigma| = 5 為字元集大小,nn 為字串長度。
  • 空間複雜度:O(n)\mathcal{O}(n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution:
def maxDifference(self, s: str, k: int) -> int:
n = len(s)
nums = list(map(int, s))
cnt = [0] * 5
for x in nums:
cnt[x] += 1

ans = -inf
for a in range(5):
for b in range(5):
if a == b or cnt[a] == 0 or cnt[b] == 0:
continue
# min(sa[j] - sb[j]),且 (sa_j, sb_j) 的奇偶性分別為 (0, 0), (0, 1), (1, 0), (1, 1)
min_sl = [inf] * 4
sa = [0] * (n + 1)
sb = [0] * (n + 1)
left = 0
for i, x in enumerate(nums, start=1):
sa[i] = sa[i - 1] + (x == a)
sb[i] = sb[i - 1] + (x == b)
while i - left >= k and sa[left] < sa[i] and sb[left] < sb[i]:
state = ((sa[left] & 1) << 1) | (sb[left] & 1)
min_sl[state] = min(min_sl[state], sa[left] - sb[left])
left += 1
# 因為 a 需要有奇數個,因此 sa[j] 的奇偶性需要與 sa[i] 相反
state = ((sa[i] & 1 ^ 1) << 1) | (sb[i] & 1)
ans = max(ans, sa[i] - sb[i] - min_sl[state])
return ans # 題目保證至少有一個合法的子字串