给你一个字符串 s 和一个整数 k 。请你找出 s 的子字符串 subs 中两个字符的出现频次之间的 最大 差值,freq[a] - freq[b] ,其中:
subs 的长度 至少 为 k 。
字符 a 在 subs 中出现奇数次。
字符 b 在 subs 中出现偶数次。
返回 最大 差值。
注意 ,subs 可以包含超过 2 个 互不相同 的字符。.
子字符串 是字符串中的一个连续字符序列。
示例 1:
输入: s = “12233”, k = 4
输出: -1
解释:
对于子字符串 "12233" ,'1' 的出现次数是 1 ,'3' 的出现次数是 2 。差值是 1 - 2 = -1 。
示例 2:
输入: s = “1122211”, k = 3
输出: 1
解释:
对于子字符串 "11222" ,'2' 的出现次数是 3 ,'1' 的出现次数是 2 。差值是 3 - 2 = 1 。
示例 3:
输入: s = “110”, k = 3
输出: -1
提示:
3 <= s.length <= 3 * 10⁴
s 仅由数字 '0' 到 '4' 组成。
输入保证至少存在一个子字符串是由一个出现奇数次的字符和一个出现偶数次的字符组成。
1 <= k <= s.length
问题分析
给定长度为 n n n 的字符串 s s s (字符集 { ′ 0 ′ , ′ 1 ′ , ′ 2 ′ , ′ 3 ′ , ′ 4 ′ } \{'0','1','2','3','4'\} { ′ 0 ′ , ′ 1 ′ , ′ 2 ′ , ′ 3 ′ , ′ 4 ′ } )和整数 k k k ,需要在所有长度至少为 k k k 的子串中,找到满足:
字符 a a a 在子串内出现奇数次;
字符 b b b 在子串内出现偶数次且至少出现一次;
最大化 f r e q ( a ) − f r e q ( b ) \mathrm{freq}(a)-\mathrm{freq}(b) freq ( a ) − freq ( b ) 。
直接枚举所有子串显然会达到 O ( n 2 ) O(n^2) O ( n 2 ) 或更高。需结合“枚举字符对 + 前缀差分 + 增量维护”将复杂度降至 O ( n ) O(n) O ( n ) 。
算法思路
整体预处理
枚举字符对 ( a , b ) (a,b) ( a , b )
共 20 对,常数级:
构造长度为 n + 1 n+1 n + 1 的差分数组D [ i ] = P [ a ] [ i ] − P [ b ] [ i ] , D[i]=P[a][i]-P[b][i],
D [ i ] = P [ a ] [ i ] − P [ b ] [ i ] ,
和状态数组S [ i ] = ( P [ a ] [ i ] m o d 2 , P [ b ] [ i ] m o d 2 ) . S[i]=\bigl(P[a][i]\bmod2,\;P[b][i]\bmod2\bigr).
S [ i ] = ( P [ a ] [ i ] mod 2 , P [ b ] [ i ] mod 2 ) .
准备字典 minD,存 4 种 ( p a , p b ) (p_a,p_b) ( p a , p b ) 状态下已纳入前缀的最小 D D D ,初始为 + ∞ +\infty + ∞ 。
使用指针 j_ptr 初始为 − 1 -1 − 1 ,表示“已纳入”的最大前缀下标。
线性扫描
对每对 ( a , b ) (a,b) ( a , b ) ,遍历 i i i 从 k k k 到 n n n :
计算此时合法起点上限
t = P [ b ] [ i ] , 若 t ≥ 1 , j max = min ( i − k , p o s [ b ] [ t − 1 ] ) ;否则 j max = − 1. t=P[b][i],\quad
\text{若 }t\ge1,\;j_{\max}=\min\bigl(i-k,\;\mathrm{pos}[b][t-1]\bigr)\text{;否则 }j_{\max}=-1.
t = P [ b ] [ i ] , 若 t ≥ 1 , j m a x = min ( i − k , pos [ b ] [ t − 1 ] ) ;否则 j m a x = − 1.
增量纳入 :
1 2 3 4 while j_ptr < j_max: j_ptr += 1 st = S[j_ptr] minD[st] = min (minD[st], D[j_ptr])
令当前终点状态为 S [ i ] = ( s a , s b ) S[i]=(s_a,s_b) S [ i ] = ( s a , s b ) ,目标状态需 a a a 奇、b b b 偶:
t a r g e t = ( s a ⊕ 1 , s b ) . \mathrm{target}=(s_a \oplus 1,\;s_b).
target = ( s a ⊕ 1 , s b ) .
若 \minD[\mathrm{target}]\neq+\infty,更新答案
a n s = max ( a n s , D [ i ] − min D [ t a r g e t ] ) . \mathrm{ans}=\max\bigl(\mathrm{ans},\;D[i]-\min D[\mathrm{target}]\bigr).
ans = max ( ans , D [ i ] − min D [ target ] ) .
时间复杂度
前缀和位置列表预处理:O ( 5 n + n ) = O ( n ) O(5n + n)=O(n) O ( 5 n + n ) = O ( n )
枚举 20 对 ( a , b ) (a,b) ( a , b ) ,每对线性扫描 O ( n ) O(n) O ( n )
总计 :O ( n ) \displaystyle O(n) O ( n )
代码分解
预处理
主循环
枚举 a,b;
构造 D、S;
初始化 minD,j_ptr;
遍历 i=k..n 做“增量纳入”与“候选更新”;
返回结果
代码实现
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class Solution : def maxDifference (self, s: str , k: int ) -> int : n = len (s) idx = lambda c: ord (c) - ord ('0' ) P = [[0 ] * (n + 1 ) for _ in range (5 )] pos = [[] for _ in range (5 )] for i, ch in enumerate (s): ci = idx(ch) pos[ci].append(i) for c in range (5 ): P[c][i + 1 ] = P[c][i] P[ci][i + 1 ] += 1 ans = float ('-inf' ) for a in range (5 ): for b in range (5 ): if a == b: continue D = [P[a][i] - P[b][i] for i in range (n + 1 )] S = [(P[a][i] & 1 , P[b][i] & 1 ) for i in range (n + 1 )] minD = { (0 , 0 ): float ('inf' ), (0 , 1 ): float ('inf' ), (1 , 0 ): float ('inf' ), (1 , 1 ): float ('inf' ), } j_ptr = -1 for i in range (k, n + 1 ): t = P[b][i] if t >= 1 : j_max = min (i - k, pos[b][t - 1 ]) else : j_max = -1 while j_ptr < j_max: j_ptr += 1 st_j = S[j_ptr] minD[st_j] = min (minD[st_j], D[j_ptr]) st_i = S[i] target = (st_i[0 ] ^ 1 , st_i[1 ]) best = minD[target] if best != float ('inf' ): ans = max (ans, D[i] - best) return ans if ans != float ('-inf' ) else -1