周赛250922
小号第一次AK, 题比较简单。
3295. 举报垃圾信息
题目大意
给定两个字符串数组 message
和 bannedWords
,你需要判断 message
是否属于“垃圾信息”。如果 message
中至少有两个单词出现在 bannedWords
中,则该数组被视为垃圾信息,返回 true
;否则返回 false
。
实现思路
- 集合操作优化:为了快速判断
message
中的单词是否在bannedWords
中,将bannedWords
转换为一个set
集合,利用集合的 O(1) 时间复杂度来快速查找单词是否存在。 - 统计匹配次数:遍历
message
数组,对每个单词检查它是否在bannedWords
集合中,记录出现在bannedWords
中的单词数。 - 判断垃圾信息:若匹配到的单词数量大于等于 2,则返回
true
(即认为message
是垃圾信息);否则返回false
。
class Solution:
def reportSpam(self, message: List[str], bannedWords: List[str]) -> bool:
bannedWords = set(bannedWords)
return sum(m in bannedWords for m in message) >= 2
Q2. 移山所需的最少秒数
题目大意
给定一个整数 mountainHeight
表示山的高度和一个数组 workerTimes
,表示工人们降低山高度所需的工作时间。工人们可以同时工作,每个工人 i 在降低山的过程中,其降低 1 个高度需要 workerTimes[i]
秒,降低 2 个高度需要 workerTimes[i] + 2 * workerTimes[i]
秒,以此类推。题目要求返回工人们将山的高度降低到 0 所需的最少秒数。
实现思路
- 工人的工作能力:工人 i 降低 x 的高度需要的时间是
workerTimes[i] * (1 + 2 + ... + x)
,即等差数列求和公式。根据时间 t,工人 i 能降低的最大高度可以通过二分查找确定。 - 二分查找最小时间:我们可以通过二分法来确定最少需要的时间,设定一个时间范围
[0, 10^16]
,每次取中间值mid
作为当前时间,判断在这段时间内,所有工人能否把山降低到 0。我们使用一个work
函数来计算工人在给定时间内可以降低的最大高度。 - 计算每个工人能降低的高度:对于每个工人,我们通过二分查找计算出当前时间内该工人能降低的最大高度,如果所有工人的总降低高度超过了山的高度,则说明当前时间
mid
可以满足需求,继续向更小的时间查找,否则向更大的时间查找。 - 最终结果:在二分法中找到的最小时间即为答案。
class Solution:
def minNumberOfSeconds(self, m: int, w: List[int]) -> int:
# 找到 当前的工作最大时间内 此工人能降低的最大高度
def work(wt, t):
lo, hi = 0, t
while lo < hi:
mid = (lo + hi + 1) // 2
if wt * mid * (mid + 1) // 2 <= t:
lo = mid
else:
hi = mid - 1
return lo
# 10^5 * (1 + 10^5) // 2 * 10 ^ 4 =
l, r = 0, 10**16
# 二分答案
while l < r:
mid = (l + r) // 2
tot = 0
for wt in w:
tot += work(wt, mid)
if tot >= m:
break
if tot >= m:
r = mid
else:
l = mid + 1
return l
Q3 & Q4 3297. 统计重新排列后包含另一个字符串的子字符串数目 I
题目大意
给定两个字符串 word1
和 word2
,如果某个子字符串 x
的重新排列能够包含 word2
作为其前缀,那么这个子字符串是合法的。问题要求返回 word1
中合法子字符串的数目。
为了满足问题中的内存限制,要求实现一个线性复杂度的解法。
实现思路
- 前缀匹配和字符计数:
- 为了判断一个子字符串的重新排列是否可以包含
word2
作为前缀,需要对word2
中的字符进行统计。 - 使用
Counter
对word2
中每个字符出现的次数进行记录,并使用滑动窗口技术遍历word1
的子字符串。
- 为了判断一个子字符串的重新排列是否可以包含
- 滑动窗口法:
- 维护一个窗口,用来记录当前窗口内的字符计数,并在窗口移动的过程中检查当前窗口能否重新排列成
word2
的前缀。 - 窗口通过左右指针扩展和收缩,每次遇到合法情况时,记录当前窗口能产生的合法子字符串的数量。
- 维护一个窗口,用来记录当前窗口内的字符计数,并在窗口移动的过程中检查当前窗口能否重新排列成
- 窗口内匹配字符数:
- 定义变量
m
,用于记录当前窗口内有多少字符已经完全匹配了word2
中对应字符的数量。当m
等于word2
中不同字符的总数时,意味着当前窗口可以重新排列出word2
的前缀。
- 定义变量
- 具体步骤:
- 初始化
word2
中字符的计数器w2_cnt
。 - 使用滑动窗口遍历
word1
,对每个窗口内的字符进行计数。如果某个字符的计数达到word2
中相应字符的数量,增加匹配字符数m
。 - 当
m
达到word2
不同字符数的总数时,表示窗口中的子字符串可以包含word2
作为前缀。 - 每当窗口合法时,计算从当前窗口右边界开始到
word1
末尾的所有子字符串都可以被重新排列成合法子字符串。
- 初始化
class Solution:
def validSubstringCount(self, w1: str, w2: str) -> int:
w2_cnt = Counter(w2)
res = 0
win = Counter()
m = 0 # the number of matched char
l = 0
for r in range(len(w1)):
if w1[r] in w2_cnt:
win[w1[r]] += 1
if win[w1[r]] == w2_cnt[w1[r]]:
m += 1
while m == len(w2_cnt):
res += len(w1) - r # (len(w1) - (r + 1) + 1) 因为r是下标
if w1[l] in w2_cnt:
if win[w1[l]] == w2_cnt[w1[l]]:
m -= 1
win[w1[l]] -= 1
l += 1
return res