记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
6/22 1189. "气球" 的最大数量
统计b,a,l,o,n,e出现的次数
取b,a,n以及l/2,o/2的最小值
python
def maxNumberOfBalloons(text):
"""
:type text: str
:rtype: int
"""
cnt = [0]*6
for c in text:
if c == 'b':
cnt[0] += 1
elif c == 'a':
cnt[1] += 1
elif c == 'n':
cnt[2] += 1
elif c == 'l':
cnt[3] += 1
elif c == 'o':
cnt[4] += 1
elif c == 'e':
cnt[5] += 1
return min(cnt[0], cnt[1], cnt[2], cnt[3]//2, cnt[4]//2)
6/23 3699. 锯齿形数组的总数 I
动态规划 + 原地前后缀累加(优化常数)
"相邻不相等" + "任意 3 个连续元素不能严格单调"
等价于:相邻比较结果必须交替(<, >, <, >... 或 >, <, >, <...)。
只统计"第一步上升(<)"这一类。
设 dpv 为当前长度下,末尾取值索引 v(0...m-1) 的方案数。
第 step 次比较时:
若需要 "<":newy = sum(oldx), x < y
可左到右原地做前缀累加得到;
若需要 ">":newy = sum(oldx), x > y
可右到左原地做后缀累加得到。
利用对称性:映射 x -> (l + r - x) 会把 "<" 与 ">" 全部互换,
因而"第一步上升"和"第一步下降"方案数相同,最终答案 = 2 * count_first_up。
python
def zigZagArrays(n, l, r):
"""
:type n: int
:type l: int
:type r: int
:rtype: int
"""
MOD = 10**9 + 7
m = r - l + 1
# 对称性:把任意值 x 映射成 (l + r - x),会把 "<" 与 ">" 全部互换,
# 因而"第一步上升"和"第一步下降"的方案数相同,只需算一边再乘 2。
# dp[v]:当前长度下,末尾取索引 v(0..m-1) 的方案数(仅统计"第一步上升")。
dp = [1] * m
# step 表示第 step 次比较(从 1 到 n-1)
for step in range(1, n):
if step & 1:
# 需要 "<":new[y] = sum(old[x], x < y)
pre = 0
for y in range(m):
old = dp[y]
dp[y] = pre
pre += old
if pre >= MOD:
pre -= MOD
else:
# 需要 ">":new[y] = sum(old[x], x > y)
suf = 0
for y in range(m - 1, -1, -1):
old = dp[y]
dp[y] = suf
suf += old
if suf >= MOD:
suf -= MOD
return (sum(dp) * 2) % MOD
6/24 3700. 锯齿形数组的总数 II
矩阵快速幂
设 m = r - l + 1,把取值映射为 0...m-1。
条件"相邻不等 + 任意 3 个连续元素不严格单调"
等价于:相邻比较符号必须交替(<,>,<,>... 或 >,<,>,<...)。
先只统计"第一步是上升(<)"的方案数,最后利用对称性乘 2:
映射 x -> (l + r - x) 会把所有 < 与 > 互换,因此两类数量相同。
定义长度为 m 的列向量 v,vy 表示当前末尾值为 y 的方案数。
U 变换(一步要求上升):newy = sum(vx), x < y
D 变换(一步要求下降):newy = sum(vx), x > y
对"第一步上升"来说,步骤序列是 U, D, U, D, ...
把两步合并:
B = D * U。若总比较次数 s = n - 1,
s = 2t: v = B^t * v0
s = 2t+1: v = U(B^t * v0)
其中 v0 = 1,1,...,1(长度 1 时末尾任意)。
所以只需计算 B^t 对向量的作用,用快速幂 O(log n)。
python
def zigZagArrays(n, l, r):
"""
:type n: int
:type l: int
:type r: int
:rtype: int
"""
MOD = 10**9 + 7
m = r - l + 1
def apply_up(vec):
"""U(vec): new[y] = sum(vec[x], x < y)"""
out = [0] * m
pre = 0
for y in range(m):
out[y] = pre
pre += vec[y]
if pre >= MOD:
pre -= MOD
return out
def mat_mul(A, B):
"""m*m 矩阵乘法"""
C = [[0] * m for _ in range(m)]
for i in range(m):
Ai = A[i]
Ci = C[i]
for k in range(m):
aik = Ai[k]
if aik == 0:
continue
Bk = B[k]
for j in range(m):
Ci[j] = (Ci[j] + aik * Bk[j]) % MOD
return C
def mat_vec_mul(A, vec):
"""m*m 矩阵乘 m 向量"""
out = [0] * m
for i in range(m):
row = A[i]
s = 0
for j in range(m):
s = (s + row[j] * vec[j]) % MOD
out[i] = s
return out
# B = D * U,且可直接写成闭式:
# B[y][x] = |{z | z>x 且 z>y}| = (m - 1 - max(x, y))
B = [[0] * m for _ in range(m)]
for y in range(m):
By = B[y]
for x in range(m):
By[x] = m - 1 - (x if x > y else y)
s = n - 1
t = s // 2
vec = [1] * m
# 快速幂:计算 vec <- B^t * vec
base = B
while t:
if t & 1:
vec = mat_vec_mul(base, vec)
base = mat_mul(base, base)
t >>= 1
# 若比较次数为奇数,还需额外做一次 U
if s & 1:
vec = apply_up(vec)
# 乘 2 补上"第一步下降"的对称方案
return (2 * (sum(vec) % MOD)) % MOD
6/25 3737. 统计主要元素子数组数目 I
枚举子数组
枚举每个子数组起点 i,再向右枚举终点 j。
在固定 i 的内层循环里,维护当前子数组 numsi...j 中 target 的出现次数 cnt。
当前子数组长度为 k = j - i + 1。
若 cnt > k // 2,则 target 是该子数组的主要元素,答案加 1。
由于只关心 target 的次数,不需要统计其他元素频次。
python
def countMajoritySubarrays(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
n = len(nums)
ans = 0
for i in range(n):
cnt = 0
for j in range(i, n):
if nums[j] == target:
cnt += 1
if cnt > (j - i + 1) // 2:
ans += 1
return ans
6/26 3739. 统计主要元素子数组数目 II
前缀和 + 树状数组
把数组按 target 转换成:
numsi == target 记为 +1
numsi != target 记为 -1
那么一个子数组中 target 为主要元素(出现次数 > 一半)
等价于该子数组和 > 0。
设前缀和为 pre。对每个位置作为右端点时,
需要统计有多少历史前缀和 old_pre 满足 old_pre < pre,
这些都对应和 > 0 的子数组。
前缀和范围在 -n, n,做一个偏移 offset = n + 1,
把它映射到 1, 2n+1,用树状数组维护"每个前缀和值出现次数":
query(idx): 前缀频次和
update(idx, +1): 插入当前前缀和
流程:
先插入空前缀和 0。
每遍历一个数更新 pre,再把 query(pre-1) 累加到答案,
最后把当前 pre 插入树状数组。
python
def countMajoritySubarrays(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
n = len(nums)
class Fenwick:
def __init__(self, size):
self.n = size
self.bit = [0] * (size + 1)
def update(self, i, delta):
while i <= self.n:
self.bit[i] += delta
i += i & -i
def query(self, i):
s = 0
while i > 0:
s += self.bit[i]
i -= i & -i
return s
size = 2 * n + 1
offset = n + 1
tree = Fenwick(size)
pre = 0
ans = 0
# 空前缀和 pre=0 先入树
tree.update(offset, 1)
for x in nums:
pre += 1 if x == target else -1
idx = pre + offset
ans += tree.query(idx - 1) # 统计历史前缀和 < 当前前缀和
tree.update(idx, 1)
return ans
6/27
python
6/28
python