记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
-
-
- [8/24 1872. 石子游戏 VIII](#8/24 1872. 石子游戏 VIII)
- [8/25 3718. 缺失的最小倍数](#8/25 3718. 缺失的最小倍数)
- [8/26 2904. 最短且字典序最小的美丽子字符串](#8/26 2904. 最短且字典序最小的美丽子字符串)
- [8/27 3720. 大于目标字符串的最小字典序排列](#8/27 3720. 大于目标字符串的最小字典序排列)
- [8/28 3734. 大于目标字符串的最小字典序回文排列](#8/28 3734. 大于目标字符串的最小字典序回文排列)
- [8/29 2948. 交换得到字典序最小的数组](#8/29 2948. 交换得到字典序最小的数组)
- [8/30 2091. 从数组中移除最大值和最小值](#8/30 2091. 从数组中移除最大值和最小值)
-
8/24 1872. 石子游戏 VIII
每次取最左边至少两枚石子,得分加上它们的和,再把这个和作为一枚新石子放回最左。
这等价于在原数组前缀和 s 上选一个下标 i,得分为 si,剩余变成 s\[i, stonesi+1, ...]。
双方轮流,当前玩家要最大化自己与对手的分数差。令 fi 表示还能选下标 >= i 时的最优分差。
只剩全部取走时 fn-1 = sn-1;否则可跳过 i 得到 fi+1,或取走 si 后对手拿 fi+1,即 fi = max(fi+1, si - fi+1)。
Alice 第一步至少取两枚,从下标 1 开始,答案为 f1。从右往左滚动更新即可。
python
def stoneGameVIII(stones):
"""
:type stones: List[int]
:rtype: int
"""
n = len(stones)
s = stones[:]
for i in range(1, n):
s[i] += s[i - 1]
f = s[n - 1]
for i in range(n - 2, 0, -1):
f = max(f, s[i] - f)
return f
8/25 3718. 缺失的最小倍数
依次便利k的倍数
python
def missingMultiple(nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
cur = k
s=set(nums)
while cur in s:
cur+=k
return cur
8/26 2904. 最短且字典序最小的美丽子字符串
如果k>1 为了使最短必定收尾为1
滑动窗口找到k个1的子串比较长度和字典序
python
def shortestBeautifulSubstring(s, k):
"""
:type s: str
:type k: int
:rtype: str
"""
if k == 1:
return "1" if "1" in s else ""
n=len(s)
left=0
while left<n and s[left]!="1":
left+=1
right=left+1
count=1
min_length=float('inf')
min_substring=""
while right<n:
if s[right] == "1":
count+=1
if count==k:
if right-left+1<min_length:
min_length=right-left+1
min_substring=s[left:right+1]
elif right-left+1==min_length and s[left:right+1]<min_substring:
min_substring=s[left:right+1]
count-=1
left+=1
while left<n and s[left]!="1":
left+=1
right+=1
return min_substring
8/27 3720. 大于目标字符串的最小字典序排列
要找 s 的重排中严格大于 target 且字典序最小的串,不存在则返回空串。
字典序最小意味着尽量长地与 target 前缀相同,再在最早允许的"变大"位置上放最小的更大字母,剩余字母升序填完。
从左到右匹配 target:若当前还能用剩余字母配出 targeti 则继续,同时记录还能换成更大字母的最右位置。
若没有任何位置能变大则无解;否则在该位置放入最小的更大字母,后面用剩余字母按 a-z 排好。
python
def lexGreaterPermutation(s, target):
"""
:type s: str
:type target: str
:rtype: str
"""
n = len(s)
cnt = [0] * 26
for ch in s:
cnt[ord(ch) - 97] += 1
rem = cnt[:]
last = -1
for i, ch in enumerate(target):
t = ord(ch) - 97
for c in range(t + 1, 26):
if rem[c]:
last = i
break
if rem[t] == 0:
break
rem[t] -= 1
if last < 0:
return ""
res = []
for i in range(last):
t = ord(target[i]) - 97
res.append(target[i])
cnt[t] -= 1
t = ord(target[last]) - 97
for c in range(t + 1, 26):
if cnt[c]:
res.append(chr(c + 97))
cnt[c] -= 1
break
for c in range(26):
if cnt[c]:
res.append(chr(c + 97) * cnt[c])
return "".join(res)
8/28 3734. 大于目标字符串的最小字典序回文排列
s 能构成回文排列当且仅当奇数次字母至多一种,否则无解。
回文由左半段决定,奇数长度时中间字母固定为那个奇数次字母,左半每种字母最多用频次的一半。
要得到严格大于 target 的最小回文,应尽量让左半与 target 前缀相同。
先按 target 左半匹配,若整段都能配出,镜像后已大于 target 则直接返回。
否则从失配位起换成最小的更大字母,剩余按升序填满左半再镜像;换不了就回退前一位。
全部回退仍无解则返回空串。
python
def lexPalindromicPermutation(s, target):
"""
:type s: str
:type target: str
:rtype: str
"""
n = len(s)
cnt = [0] * 26
for ch in s:
cnt[ord(ch) - 97] += 1
mid = -1
for i, c in enumerate(cnt):
if c % 2:
if mid >= 0:
return ""
mid = i
cnt[i] //= 2
half = n // 2
def make(left):
if n % 2:
return left + chr(mid + 97) + left[::-1]
return left + left[::-1]
left = []
pos = 0
while pos < half:
t = ord(target[pos]) - 97
if cnt[t] == 0:
break
left.append(target[pos])
cnt[t] -= 1
pos += 1
if pos == half:
cand = make("".join(left))
if cand > target:
return cand
while True:
if pos < half:
t = ord(target[pos]) - 97
for c in range(t + 1, 26):
if cnt[c]:
cnt[c] -= 1
rest = [chr(c + 97)]
for i in range(26):
rest.append(chr(i + 97) * cnt[i])
return make("".join(left) + "".join(rest))
if pos == 0:
return ""
pos -= 1
cnt[ord(left.pop()) - 97] += 1
8/29 2948. 交换得到字典序最小的数组
只要 |a-b|<=limit 就可以交换,经若干次后,按值排序后相邻差都不超过 limit 的数会落在同一连通块,块内可任意重排。
把 (值, 下标) 按值排序,相邻差 > limit 处切开,每个连通块内把排序后的值依次填回排序后的原下标,整体就是字典序最小数组。
python
def lexicographicallySmallestArray(nums, limit):
"""
:type nums: List[int]
:type limit: int
:rtype: List[int]
"""
n = len(nums)
arr = sorted(zip(nums, range(n)))
ans = [0] * n
i = 0
while i < n:
j = i + 1
while j < n and arr[j][0] - arr[j - 1][0] <= limit:
j += 1
idx = sorted(k for _, k in arr[i:j])
for k, (x, _) in zip(idx, arr[i:j]):
ans[k] = x
i = j
return ans
8/30 2091. 从数组中移除最大值和最小值
遍历一遍数据 找到最大最小值位置
去除一共三种情况
都从左边取出
都从右边取出
分别从左右取出
python
def minimumDeletions(nums):
"""
:type nums: List[int]
:rtype: int
"""
maxv,minv = float('-inf'),float('inf')
maxloc,minloc = -1,-1
for i,num in enumerate(nums):
if num>maxv:
maxv=num
maxloc = i
if num<minv:
minv=num
minloc = i
n = len(nums)
left = min(maxloc,minloc)
right = max(maxloc,minloc)
print(n,left,right)
ans = min(right+1,n-left,left+1+n-right)
return ans