记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
3/30 2840. 判断通过操作能否让字符串相等 II
奇数位偶数位分别统计
先遍历奇数位 记录s1中字符出现次数是否和s2相同 cnt1[x]记录字符x出现次数 在s1中+1 在s2中-1
再遍历偶数位 记录s1中字符出现次数是否和s2相同 cnt2同上
python
def checkStrings(s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
cnt1 = [0]*26
cnt2 = [0]*26
for i in range(len(s1)):
if i%2:
cnt1[ord(s1[i])-ord('a')]+=1
cnt1[ord(s2[i])-ord('a')]-=1
else:
cnt2[ord(s1[i])-ord('a')]+=1
cnt2[ord(s2[i])-ord('a')]-=1
return all(x==0 for x in cnt1) and all(x==0 for x in cnt2)
3/31 3474. 字典序最小的生成字符串
先处理T窗口的强制匹配
再处理F窗口: 若当前仍可能完全等于str2 则必须在该窗口内挑一个未强制位置改成不等字符
为保证字典序最小 每次优先改最靠右的位置
python
def generateString(str1, str2):
"""
:type str1: str
:type str2: str
:rtype: str
"""
n, m = len(str1), len(str2)
L = n + m - 1
ans = ["?"] * L
forced = [False] * L
# 1) 先满足所有T窗口
for i, flag in enumerate(str1):
if flag != "T":
continue
for j in range(m):
p = i + j
c = str2[j]
if ans[p] != "?" and ans[p] != c:
return ""
ans[p] = c
forced[p] = True
# 2) 未强制位置先填'a',得到当前字典序最小串
for i in range(L):
if ans[i] == "?":
ans[i] = "a"
# 3) 处理F窗口:仅当窗口刚好等于str2时,才在最右可改位改成'b'
for i, flag in enumerate(str1):
if flag != "F":
continue
ok = False
for j in range(m):
if ans[i + j] != str2[j]:
ok = True
break
if ok:
continue
changed = False
for j in range(m - 1, -1, -1):
p = i + j
if not forced[p]:
ans[p] = "b"
changed = True
break
if not changed:
return ""
res = "".join(ans)
# 4) 最终校验
for i, flag in enumerate(str1):
sub = res[i:i + m]
if (flag == "T" and sub != str2) or (flag == "F" and sub == str2):
return ""
return res
4/1 2751. 机器人碰撞
按位置从小到大处理 只会发生R与后续L的碰撞
用栈维护尚未碰撞掉的向右机器人(下标)
遇到L机器人时不断与栈顶R机器人对撞:
health大者减1并存活 health相等则同归于尽
python
def survivedRobotsHealths(positions, healths, directions):
"""
:type positions: List[int]
:type healths: List[int]
:type directions: str
:rtype: List[int]
"""
n = len(positions)
ids = sorted(range(n), key=lambda i: positions[i])
h = healths[:]
alive = [True] * n
stack = []
for i in ids:
if directions[i] == "R":
stack.append(i)
continue
# 当前是L,尝试与之前的R碰撞
while stack and alive[i]:
j = stack[-1]
if not alive[j]:
stack.pop()
continue
if h[j] < h[i]:
alive[j] = False
h[i] -= 1
stack.pop()
elif h[j] > h[i]:
alive[i] = False
h[j] -= 1
else:
alive[j] = False
alive[i] = False
stack.pop()
return [h[i] for i in range(n) if alive[i]]
4/2 3418. 机器人可以获得的最大金币数
迭代 DP:
dp[i][j][k] 表示走到(i,j)且已感化k次(0<=k<=2)时的最大金币。
每个格子两种选择:
- 不感化:加上 coins[i][j]
- 若 coins[i][j] < 0 且 k < 2,可感化:该格子按 0 计,并将感化次数 +1
python
def maximumAmount(coins):
"""
:type coins: List[List[int]]
:rtype: int
"""
m, n = len(coins), len(coins[0])
NEG = -10**18
# 仅保留上一行,空间 O(n*3)
prev = [[NEG, NEG, NEG] for _ in range(n)]
for i in range(m):
cur = [[NEG, NEG, NEG] for _ in range(n)]
for j in range(n):
v = coins[i][j]
in0 = in1 = in2 = NEG
# 起点:进入起点前金币为0,感化次数为0
if i == 0 and j == 0:
in0 = 0
# 来自上方
if i > 0:
up = prev[j]
if up[0] > in0:
in0 = up[0]
if up[1] > in1:
in1 = up[1]
if up[2] > in2:
in2 = up[2]
# 来自左方
if j > 0:
left = cur[j - 1]
if left[0] > in0:
in0 = left[0]
if left[1] > in1:
in1 = left[1]
if left[2] > in2:
in2 = left[2]
out = cur[j]
# 不感化当前格子
if in0 != NEG:
out[0] = in0 + v
if in1 != NEG:
out[1] = in1 + v
if in2 != NEG:
out[2] = in2 + v
# 感化当前负数格子:该格子记0,并消耗一次感化
if v < 0:
if in0 > out[1]:
out[1] = in0
if in1 > out[2]:
out[2] = in1
prev = cur
end = prev[n - 1]
return max(end[0], end[1], end[2])
4/3 3661. 可以被机器人摧毁的最大墙壁数目
1.先将机器人按位置排序,墙位置也排序,便于后续用二分统计区间内墙数。
2.每个机器人只能左射或右射。左射区间为 [x-d, x],但会被左侧最近机器人阻挡,
所以左端应修正为 max(x-d, prev_robot_x+1)
3 右射区间为 [x, x+d],会被右侧最近机器人阻挡。并且右侧机器人若选择左射,
还会额外占用一段左射程,因此要区分两种"右邻状态"。
4.设 dp(i, j):
i 表示处理到第 i 个机器人
j=0/1 表示右侧相邻机器人对应的状态(影响当前机器人右射上界)
转移时在"当前左射"和"当前右射"中取最大值。
5.区间墙数通过二分统计:
count([l, r]) = lower_bound(r+1) - lower_bound(l)。
6.用滚动变量迭代 DP(prev0, prev1)替代递归,避免递归深度问题。
python
def maxWalls(robots, distance, walls):
"""
:type robots: List[int]
:type distance: List[int]
:type walls: List[int]
:rtype: int
"""
from bisect import bisect_left
n = len(robots)
arr = sorted(zip(robots, distance)) # (位置, 射程)
walls.sort()
def count_in_range(l, r):
if l > r:
return 0
return bisect_left(walls, r + 1) - bisect_left(walls, l)
# prev0 = dfs(i-1, 0), prev1 = dfs(i-1, 1)
# 其中 dfs(i, j) 表示处理到第 i 个机器人时,且"右侧相邻机器人方向状态"为 j 的最优值
prev0 = 0
prev1 = 0
for i in range(n):
x, d = arr[i]
# 当前机器人向左射击,子弹会被左侧最近机器人阻挡
left_l = x - d
if i > 0:
left_l = max(left_l, arr[i - 1][0] + 1)
left_cnt = count_in_range(left_l, x)
# 当前机器人向右射击,右端受右侧机器人影响
# j = 0: 右侧相邻机器人向左,需额外避开其左射程
# j = 1: 右侧相邻机器人向右,只需避开其位置
right0 = x + d
right1 = x + d
if i + 1 < n:
nx, nd = arr[i + 1]
right0 = min(right0, nx - nd - 1)
right1 = min(right1, nx - 1)
right_cnt0 = count_in_range(x, right0)
right_cnt1 = count_in_range(x, right1)
cur0 = max(prev0 + left_cnt, prev1 + right_cnt0)
cur1 = max(prev0 + left_cnt, prev1 + right_cnt1)
prev0, prev1 = cur0, cur1
return prev1
4/4
python
4/5
python