题目描述
思路解析
一句话答案:LeetCode 2327 知道秘密的人数用 DP:dp[d] 记第 d 天新学会的人数,等于窗口 [d-forget+1, d-delay] 内 dp 之和;答案是最近 forget 天之和取模,滑动窗口和维护,时间、空间均 O(n)。
第 1 天只有 1 个人,第 6 天怎么就 5 个了
第 1 天有 1 个人知道秘密;每个学会秘密的人满 delay 天后开始每天分享给 1 个新人,学会满 forget 天彻底忘记。求第 n 天结束时还知道秘密的总人数,对 10^9+7 取模(取模只留除后余数,防装不下)。题面示例 n=6、delay=2、forget=4 时答案是 5。
一人一张档案挨个模拟,为什么撑不到第 n 天
逐个人头模拟——每人一张学会日档案,每天翻一遍看谁开讲、谁忘记。新人过 delay 天又拉新人,档案成倍往上堆,天数一多就翻不完。可档案里有用的只有学会日:学会日相同的人,开讲、忘记日全一样。
同一天学会的人,为什么能捆成一格记
按学会日分桶,动态规划(DP,把每天新学会几人存进表,用时直接查)里定义 dp[d] 为第 d 天新学会秘密的人数,这一格就是本题的状态。第 1 天只有最初那人,dp[1]=1 是种子。
窗口两端 d-forget+1 和 d-delay 从哪来
第 d 天新学会几人,等于当天正在开讲的人数(每人当天恰好教会 1 个新人)。谁在第 d 天开讲?学会日 j 得同时满足:首次分享日 j+delay ≤ d,即 j ≤ d-delay(右端);还没忘 d ≤ j+forget-1,即 j ≥ d-forget+1(左端)。能开讲的人学会日恰好落在窗口 [d-forget+1, d-delay] 里。
转移(由前面的格子推出当前格的规则)就一句:dp[d] 等于窗口内所有 dp[j] 之和。窗口两端各错一格,数字立刻不对。
n=6、delay=2、forget=4,答案 5 怎么冒出来
dp[1]=1。第 2 天窗口 [2-4+1, 2-2]=[-1, 0],全在第 1 天前,dp[2]=0。第 3 天窗口 [0, 1] 只有 dp[1]=1,dp[3]=1。第 4 天窗口 [1, 2],dp[1]+dp[2]=1+0=1,dp[4]=1。第 5 天窗口 [2, 3],dp[2]+dp[3]=0+1=1,dp[5]=1。第 6 天窗口 [3, 4],dp[3]+dp[4]=1+1=2,dp[6]=2。
收答案:第 6 天记得秘密的,学会日落在 [6-4+1, 6]=[3, 6],dp[3]+dp[4]+dp[5]+dp[6]=1+1+1+2=5,正是示例 1 的答案。
第 2 天窗口是 [-1, 0],负下标一取会出什么事
每天把窗口里 forget-delay 个数相加是 O(n·(forget-delay));窗口每天右移一格,改成维护一条滑动窗口和(右移时加新格、减掉滑出的格)可降到 O(n)。空间 O(n),存整条 dp。
负下标要防:前几天窗口会伸到第 1 天之前,像第 2 天的 [-1, 0],求和下界卡在 1、负下标按 0 算。取模也别漏:C++ 和 Java 每步取模、减法先补 mod 再取余,免得减出负数。答案那段用题面示例 2 复核:n=4、delay=1、forget=3 时 dp[2]+dp[3]+dp[4]=1+2+3=6(都按窗口和算:dp[2]=dp[1]=1、dp[3]=dp[1]+dp[2]=2、dp[4]=dp[2]+dp[3]=3)。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住两句:新学会 dp[i] = 窗口 [i-forget+1, i-delay] 的新增之和;最终答案 = 最近 forget 天新学会的人数之和。下面每帧都在套它。
开局:第 1 天有 1 个人知道秘密,所以 dp[1]=1(绿色)。下标 0 是占位守卫(灰),恒为 0。从第 2 天起,每天的新增都要靠前面几天推出来。
推 第 2 天 的新增 dp[2]。窗口 [-1, 0] 整个落在第 1 天之前,还没有人到了能分享的日子,所以今天没有新学会的人。
把结果填进去:dp[2]=0(绿色)。这一格从此定死,作为后面更晚那些天的贡献来源。
推 第 3 天 的新增 dp[3]。能在今天教新人的,是学会日落在第 1 到 1 天(黄框)的人:更早的已经忘、更晚的还没到分享日。
把黄框里这几天的新增加起来:dp[1]=1,一共 1 人。也就是说 第 3 天 会新学会 1 个人。
把结果填进去:dp[3]=1(绿色)。这一格从此定死,作为后面更晚那些天的贡献来源。
推 第 4 天 的新增 dp[4]。能在今天教新人的,是学会日落在第 1 到 2 天(黄框)的人:更早的已经忘、更晚的还没到分享日。
把黄框里这几天的新增加起来:dp[1]=1,dp[2]=0,一共 1 人。也就是说 第 4 天 会新学会 1 个人。
把结果填进去:dp[4]=1(绿色)。这一格从此定死,作为后面更晚那些天的贡献来源。
推 第 5 天 的新增 dp[5]。能在今天教新人的,是学会日落在第 2 到 3 天(黄框)的人:更早的已经忘、更晚的还没到分享日。
把黄框里这几天的新增加起来:dp[2]=0,dp[3]=1,一共 1 人。也就是说 第 5 天 会新学会 1 个人。
把结果填进去:dp[5]=1(绿色)。这一格从此定死,作为后面更晚那些天的贡献来源。
推 第 6 天 的新增 dp[6]。能在今天教新人的,是学会日落在第 3 到 4 天(黄框)的人:更早的已经忘、更晚的还没到分享日。
把黄框里这几天的新增加起来:dp[3]=1,dp[4]=1,一共 2 人。也就是说 第 6 天 会新学会 2 个人。
把结果填进去:dp[6]=2(绿色)。这一格从此定死,作为后面更晚那些天的贡献来源。
到这里,1 到 6 天每天新学会多少人都推完了。但注意 dp 是「当天新学会」的人数,不是总人数。第 6 天还知道秘密的,只有最近 forget=4 天里学会、还没忘的人。
第 6 天结束时,只有第 3 到 6 天(黄框)学会的人还没忘、仍知道秘密。更早学会的(第 1、2 天)满 forget 天已经忘了。把黄框里的 dp 加起来即答案。
加上第 3 天新学会的 dp[3]=1 人,running 小计到 1。绿色是已经数进答案的天。
加上第 4 天新学会的 dp[4]=1 人,running 小计到 2。绿色是已经数进答案的天。
加上第 5 天新学会的 dp[5]=1 人,running 小计到 3。绿色是已经数进答案的天。
加上第 6 天新学会的 dp[6]=2 人,running 小计到 5。绿色是已经数进答案的天。
最近 4 天学会的人加满,一共 5 人,这就是第 6 天知道秘密的总人数,答案 5。和题面示例 1 的 5 完全对上。
三组边界都可以自己按「窗口求和 + 最近 forget 天累加」手推验证。
两个高频追问:区间端点的开闭理由,以及差分数组的统计优势。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightclass ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def peopleAwareOfSecret(self, n: int, delay: int, forget: int) -> int: m = (n << 1) + 10 d = [0] * m cnt = [0] * m cnt[1] = 1 for i in range(1, n + 1): if cnt[i]: d[i] += cnt[i] d[i + forget] -= cnt[i] nxt = i + delay while nxt < i + forget: cnt[nxt] += cnt[i] nxt += 1 mod = 10**9 + 7 return sum(d[: n + 1]) % mod复杂度
- 时间:O(n·(forget−delay)),每天要把窗口内 forget−delay 个数相加;用滑动窗口维护跑动和可降到 O(n)
- 空间:O(n),开长度约 2n 的 dp/差分数组
易错点
面试追问把动画讲成自己的话
追问分享区间为什么是左闭右开 [i+delay, i+forget)?
追问参考代码为什么用差分数组统计答案,而不是每天遍历一遍?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
统计构造好字符串的方案数
LeetCode 2466 · 中等 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题