Skip to content

Commit b0326eb

Browse files
committed
feat(algorithms, dynamic-programming): number of people aware of a secret
1 parent 86d1c7a commit b0326eb

12 files changed

Lines changed: 232 additions & 0 deletions
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Number of People Aware of a Secret
2+
3+
You are given an integer delay, which means that each person will share the secret with a new person every day, starting
4+
from delay days after discovering the secret. You are also given an integer forget, which means that each person will
5+
forget the secret forget days after discovering it. A person cannot share the secret on the same day they forgot it, or
6+
on any day afterwards.
7+
8+
Given an integer n, return the number of people who know the secret at the end of day n. Since the answer may be very
9+
large, return it modulo 10^9 + 7.
10+
11+
## Examples
12+
13+
![Example 1](./images/examples/number_of_people_aware_of_a_secret_example_1.png)
14+
![Example 2](./images/examples/number_of_people_aware_of_a_secret_example_2.png)
15+
![Example 3](./images/examples/number_of_people_aware_of_a_secret_example_3.png)
16+
17+
Example 4:
18+
19+
```text
20+
Input: n = 6, delay = 2, forget = 4
21+
Output: 5
22+
Explanation:
23+
Day 1: Suppose the first person is named A. (1 person)
24+
Day 2: A is the only person who knows the secret. (1 person)
25+
Day 3: A shares the secret with a new person, B. (2 people)
26+
Day 4: A shares the secret with a new person, C. (3 people)
27+
Day 5: A forgets the secret, and B shares the secret with a new person, D. (3 people)
28+
Day 6: B shares the secret with E, and C shares the secret with F. (5 people)
29+
```
30+
31+
Example 5:
32+
33+
```text
34+
Input: n = 4, delay = 1, forget = 3
35+
Output: 6
36+
Explanation:
37+
Day 1: The first person is named A. (1 person)
38+
Day 2: A shares the secret with B. (2 people)
39+
Day 3: A and B share the secret with 2 new people, C and D. (4 people)
40+
Day 4: A forgets the secret. B, C, and D share the secret with 3 new people. (6 people)
41+
```
42+
43+
## Constraints
44+
45+
- 2 <= n <= 1000
46+
- 1 <= delay < forget <= n
47+
48+
## Topics
49+
50+
- Dynamic Programming
51+
- Queue
52+
- Simulation
53+
54+
## Hints
55+
56+
- Let `dp[i][j]` be the number of people who have known the secret for exactly j + 1 days, at day i.
57+
- If `j > 0`, `dp[i][j] = dp[i – 1][j – 1]`.
58+
- `dp[i][0] = sum(dp[i – 1][j])` `for j in [delay – 1, forget – 2]`.
59+
60+
## Solution
61+
62+
1. [Simulation + Deque](#simulation--deque)
63+
2. [Dynamic Programming](#dynamic-programming)
64+
65+
### Simulation + Deque
66+
67+
We can directly simulate the process based on the description of the problem.
68+
69+
We use two deques, `know` and `share`, which represent people who know the secret (but will not share it) and people who
70+
will share the secret, respectively. Each element in these two deques is a tuple `(day,cnt)`, where day indicates the
71+
day the secret becomes known, and `cnt` represents the number of people who know the secret on that day.
72+
73+
Initially, on the first day, only one person knows the secret and will not share it, so `know=[(1,1)]` and `share=[]`.
74+
75+
On the i-th day `(2 ≤ i ≤ n)`:
76+
77+
1. On the (`i−delay`)-th day, people who knew the secret start to share it. Therefore, if the first element of `know` is
78+
`(i−delay,cnt)`, remove it and add it to the end of `share`.
79+
2. On the `(i−forget)`-th day, people who learned the secret forget it. Therefore, if the first element of share is
80+
`(i−forget,cnt)`, remove it.
81+
3. Everyone in `share` teaches the secret to new people. Therefore, we add `(i,cnt)` to the end of `know`, where `cnt`
82+
is the sum of all counts in `share`.
83+
84+
The time complexity of steps 1 and 2 is `O(1)`, while step 3 is `O(n)` because it requires traversing share. Although
85+
this is sufficient to solve the problem within the limits, we can optimize it to `O(1)`. We can maintain two variables,
86+
`knowcnt` and `sharecnt`, to store the sum of counts in `know` and `share`, respectively. In this way, the time
87+
complexity of steps 1 and 2 remains `O(1)`, and step 3 is reduced to `O(1)` as well.
88+
89+
The space complexity evaluates to `O(n)`. The deques require up to `O(n)` space to store the elements.
90+
91+
The final answer is `knowcnt` + `sharecnt`.
92+
93+
---
94+
95+
### Dynamic Programming
96+
97+
The key insight is to use dynamic programming where `dp[i]` represents the number of people who discover the secret on
98+
day `i`. On day 1, exactly one person discovers the secret. For each subsequent day `i`, the number of new people who
99+
learn the secret equals the total number of “active sharers” on that day. A person who discovered the secret on day `d`
100+
is an active sharer on day `i` if they have passed their delay period (`d + delay <= i`) and have not yet forgotten
101+
(`d + forget > i`). Instead of recalculating the sum of all active sharers from scratch each day, we maintain a running
102+
`sharing` variable that we update incrementally: when day `i` arrives, people who discovered the secret on `day i - delay`
103+
begin sharing (add `dp[i - delay]`), and people who discovered it on `day i - forget` stop sharing because they forget
104+
(subtract `dp[i - forget]`). After filling the entire dp array, the final answer is the sum of d`p[i]` for all days `i`
105+
where the person has not yet forgotten the secret by day `n`, meaning `i + forget > n`.
106+
107+
Now, let’s look at the solution steps below:
108+
109+
1. Initialize a constant `MOD` equal to 10^9 + 7 for modular arithmetic.
110+
2. Create an array `dp` of size `n + 1`, initialized to 0, where `dp[i]` represents the number of people who discover
111+
the secret on day `i`.
112+
3. Set `dp[1] = 1` since exactly one person discovers the secret on day 1.
113+
4. Initialize a variable `sharing` to 0 to track the running count of people currently able to share the secret.
114+
5. Iterate over each day `i` from 2 to `n`:
115+
- If `i - delay >= 1`, add `dp[i - delay]` to sharing (modulo `MOD`), because people who discovered the secret on
116+
day `i - delay` have now waited long enough and begin sharing today.
117+
- If `i - forget >= 1`, subtract `dp[i - forget]` from sharing (modulo `MOD`), because people who discovered the
118+
secret on day `i - forget` forget it today and stop sharing.
119+
- Set `dp[i]` to the current value of sharing modulo `MOD`, representing all new people who learn the secret on day `i`.
120+
6. After filling the `dp` array, compute the final result by summing `dp[i]` for all days `i` from 1 to n where`i + forget > n`.
121+
- This condition ensures we only count people who have not yet forgotten the secret by the end of day `n`.
122+
7. Return result modulo `MOD`.
123+
124+
![Solution 1](./images/solutions/number_of_people_aware_of_a_secret_solution_1.png)
125+
![Solution 2](./images/solutions/number_of_people_aware_of_a_secret_solution_2.png)
126+
![Solution 3](./images/solutions/number_of_people_aware_of_a_secret_solution_3.png)
127+
![Solution 4](./images/solutions/number_of_people_aware_of_a_secret_solution_4.png)
128+
![Solution 5](./images/solutions/number_of_people_aware_of_a_secret_solution_5.png)
129+
![Solution 6](./images/solutions/number_of_people_aware_of_a_secret_solution_6.png)
130+
131+
#### Time Complexity
132+
133+
The time complexity of the solution is `O(n)` because we iterate through all days from 2 to n once to fill the dp array,
134+
and then iterate from 1 to n once more to compute the final result. Each iteration performs constant time operations.
135+
136+
#### Space Complexity
137+
138+
The space complexity of the solution is `O(n)` because we use a dp array of size n + 1 to store the number of people who
139+
discover the secret on each day.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from collections import deque
2+
from typing import Tuple, Deque
3+
4+
5+
def people_aware_of_secret(n: int, delay: int, forget: int) -> int:
6+
mod = 10**9 + 7
7+
# dp[i] represents the number of people who discover the secret on day i
8+
dp = [0] * (n + 1)
9+
# Day 1: one person discovers the secret
10+
dp[1] = 1
11+
# Track the number of people currently able to share (active sharers)
12+
sharing = 0
13+
# Fill dp for each day from 2 to n
14+
for i in range(2, n + 1):
15+
# A person who discovered on day (i - delay) starts sharing today
16+
if i - delay >= 1:
17+
sharing = (sharing + dp[i - delay]) % mod
18+
# A person who discovered on day (i - forget) stops sharing today (they forget)
19+
if i - forget >= 1:
20+
sharing = (sharing - dp[i - forget]) % mod
21+
# New people who discover the secret today equals current active sharers
22+
dp[i] = sharing % mod
23+
# Sum up all people who still know the secret at end of day n
24+
# A person discovered on day d still knows it if d + forget > n, i.e., d > n - forget
25+
result = 0
26+
for i in range(1, n + 1):
27+
# Person discovered on day i forgets on day i + forget, so they know it on day n if i + forget > n
28+
if i + forget > n:
29+
result = (result + dp[i]) % mod
30+
return result
31+
32+
33+
def people_aware_of_secret_simulation_deque(n: int, delay: int, forget: int) -> int:
34+
know: Deque[Tuple[int, int]] = deque([(1, 1)])
35+
share: Deque[Tuple[int, int]] = deque([])
36+
know_cnt, share_cnt = 1, 0
37+
38+
for i in range(2, n + 1):
39+
if know and know[0][0] == i - delay:
40+
know_cnt -= know[0][1]
41+
share_cnt += know[0][1]
42+
share.append(know[0])
43+
know.popleft()
44+
if share and share[0][0] == i - forget:
45+
share_cnt -= share[0][1]
46+
share.popleft()
47+
if share:
48+
know_cnt += share_cnt
49+
know.append((i, share_cnt))
50+
return (know_cnt + share_cnt) % (10**9 + 7)
53.5 KB
Loading
46.3 KB
Loading
45.2 KB
Loading
Loading
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)