Skip to content

Commit 6c5e167

Browse files
924 Minimize Malware Spread.py
1 parent ced9db6 commit 6c5e167

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

924 Minimize Malware Spread.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/python3
2+
"""
3+
In a network of nodes, each node i is directly connected to another node j if
4+
and only if graph[i][j] = 1.
5+
6+
Some nodes initial are initially infected by malware. Whenever two nodes are
7+
directly connected and at least one of those two nodes is infected by malware,
8+
both nodes will be infected by malware. This spread of malware will continue
9+
until no more nodes can be infected in this manner.
10+
11+
Suppose M(initial) is the final number of nodes infected with malware in the
12+
entire network, after the spread of malware stops.
13+
14+
We will remove one node from the initial list. Return the node that if removed,
15+
would minimize M(initial). If multiple nodes could be removed to minimize
16+
M(initial), return such a node with the smallest index.
17+
18+
Note that if a node was removed from the initial list of infected nodes, it may
19+
still be infected later as a result of the malware spread.
20+
21+
Example 1:
22+
Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
23+
Output: 0
24+
25+
Example 2:
26+
Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
27+
Output: 0
28+
29+
Example 3:
30+
Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
31+
Output: 1
32+
33+
Note:
34+
1 < graph.length = graph[0].length <= 300
35+
0 <= graph[i][j] == graph[j][i] <= 1
36+
graph[i][i] = 1
37+
1 <= initial.length < graph.length
38+
0 <= initial[i] < graph.length
39+
"""
40+
from typing import List
41+
from collections import defaultdict
42+
43+
44+
class DisjointSet:
45+
def __init__(self):
46+
self.pi = {}
47+
48+
def union(self, x, y):
49+
pi_x = self.find(x)
50+
pi_y = self.find(y)
51+
self.pi[pi_x] = pi_y
52+
53+
def find(self, x):
54+
if x not in self.pi:
55+
self.pi[x] = x
56+
if self.pi[x] != x:
57+
self.pi[x] = self.find(self.pi[x])
58+
return self.pi[x]
59+
60+
61+
class Solution:
62+
def minMalwareSpread(self, graph: List[List[int]], initial: List[int]) -> int:
63+
"""
64+
DisjointSet. But how to use DisjointSet?
65+
66+
Ensure each component, the element points to a common ancestor.
67+
The ancestor uniquely identify the component
68+
69+
Each component has size. If there are only one malware in the component,
70+
then the component can be sanitized.
71+
"""
72+
ds = DisjointSet()
73+
n = len(graph) # nbr matrix
74+
for i in range(n):
75+
for j in range(n):
76+
if graph[i][j] == 1:
77+
ds.union(i, j)
78+
79+
counts = defaultdict(int) # count of element in the component
80+
for i in range(n):
81+
counts[ds.find(i)] += 1
82+
83+
malware_counts = defaultdict(int)
84+
for i in initial:
85+
malware_counts[ds.find(i)] += 1
86+
87+
max_i = min(initial)
88+
for i in initial:
89+
pi = ds.find(i)
90+
if malware_counts[pi] == 1:
91+
max_count = counts[ds.find(max_i)]
92+
if max_count < counts[pi]:
93+
max_i = i
94+
elif max_count == counts[pi] and max_i > i:
95+
max_i = i
96+
97+
return max_i
98+
99+
100+
if __name__ == "__main__":
101+
assert Solution().minMalwareSpread([[1,1,0],[1,1,0],[0,0,1]], [0,1]) == 0

0 commit comments

Comments
 (0)