Skip to content

Commit b5ea92c

Browse files
committed
LeetCode 990. Satisfiability of Equality Equations
1 parent a7869aa commit b5ea92c

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
212212
| [968. Binary Tree Cameras][lc968] | 🔴 Hard | [![python](res/py.png)][lc968py] |
213213
| [985. Sum of Even Numbers After Queries][lc985] | 🟠 Medium | [![python](res/py.png)][lc985py] |
214214
| [987. Vertical Order Traversal of a Binary Tree][lc987] | 🔴 Hard | [![python](res/py.png)][lc987py] |
215+
| [990. Satisfiability of Equality Equations][lc990] | 🟠 Medium | [![python](res/py.png)][lc990py] |
215216
| [994. Rotting Oranges][lc994] | 🟠 Medium | [![python](res/py.png)][lc994py] |
216217
| [1041. Robot Bounded In Circle][lc1041] | 🟠 Medium | [![python](res/py.png)][lc1041py] |
217218
| [1046. Last Stone Weight][lc1046] | 🟢 Easy | [![python](res/py.png)][lc1046py] |
@@ -636,6 +637,8 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
636637
[lc985py]: leetcode/sum-of-even-numbers-after-queries.py
637638
[lc987]: https://leetcode.com/problems/vertical-order-traversal-of-a-binary-tree/
638639
[lc987py]: leetcode/vertical-order-traversal-of-a-binary-tree.py
640+
[lc990]: https://leetcode.com/problems/satisfiability-of-equality-equations/
641+
[lc990py]: leetcode/satisfiability-of-equality-equations.py
639642
[lc994]: https://leetcode.com/problems/rotting-oranges/
640643
[lc994py]: leetcode/rotting-oranges.py
641644
[lc1041]: https://leetcode.com/problems/robot-bounded-in-circle/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# 990. Satisfiability of Equality Equations
2+
# 🟠 Medium
3+
#
4+
# https://leetcode.com/problems/satisfiability-of-equality-equations/
5+
#
6+
# Tags: Array - String - Union Find - Graph
7+
8+
import timeit
9+
from string import ascii_lowercase
10+
from typing import List
11+
12+
13+
# Use union find, iterate once over the equations using equality
14+
# functions to determine which symbols belong on the same disjoint set,
15+
# then iterate again over the equations using inequality functions to
16+
# determine if any two symbols that need to be unequal belong to the
17+
# same disjoint set, if any pair does, return False.
18+
#
19+
# Time complexity: O(n) - We iterate over the input twice, for each
20+
# element we do O(1) operations in both, the union find algorithm is
21+
# amortized O(1) but, since it has a max depth of 26, we can see
22+
# consider it O(1).
23+
# Space complexity: O(1) - We keep a dictionary of operand: value pairs
24+
# that can grow to size 26, the number of possible operands.
25+
#
26+
# Runtime: 63 ms, faster than 73.09%
27+
# Memory Usage: 14.1 MB, less than 69.95%
28+
class UnionFind:
29+
def equationsPossible(self, equations: List[str]) -> bool:
30+
# A dictionary of characters pointing to their disjoint set
31+
# parent.
32+
parents = dict(zip(ascii_lowercase, ascii_lowercase))
33+
34+
# Define a function that finds the parent of a given character.
35+
def findParent(char: str) -> str:
36+
if parents[char] == char:
37+
return char
38+
parents[char] = findParent(parents[char])
39+
return parents[char]
40+
41+
# Define a function that merges two characters into the same set.
42+
def group(a: str, b: str) -> None:
43+
parents[findParent(a)] = findParent(b)
44+
45+
# Iterate over the input equations using equality functions to
46+
# create a disjoint set structure.
47+
for l, e, _, r in equations:
48+
if e == "=":
49+
group(l, r)
50+
51+
# Iterate again over the input equations, this time using
52+
# inequality functions to check that two unequal elements do
53+
# not belong to the same set.
54+
for l, e, _, r in equations:
55+
if e == "!" and findParent(l) == findParent(r):
56+
return False
57+
58+
# If we didn't find any contradiction, return True
59+
return True
60+
61+
62+
def test():
63+
executors = [UnionFind]
64+
tests = [
65+
[["b!=b"], False],
66+
[["a==b", "b!=a"], False],
67+
[["b==a", "a==b"], True],
68+
[
69+
["a==b", "c!=a", "c==d", "e!=f", "z==a", "z!=v", "m!=o", "o==a"],
70+
True,
71+
],
72+
[
73+
["a==b", "c!=a", "c==d", "e!=f", "d==a", "z!=v", "m!=o", "o==a"],
74+
False,
75+
],
76+
]
77+
for executor in executors:
78+
start = timeit.default_timer()
79+
for _ in range(1):
80+
for col, t in enumerate(tests):
81+
sol = executor()
82+
result = sol.equationsPossible(t[0])
83+
exp = t[1]
84+
assert result == exp, (
85+
f"\033[93m» {result} <> {exp}\033[91m for"
86+
+ f" test {col} using \033[1m{executor.__name__}"
87+
)
88+
stop = timeit.default_timer()
89+
used = str(round(stop - start, 5))
90+
cols = "{0:20}{1:10}{2:10}"
91+
res = cols.format(executor.__name__, used, "seconds")
92+
print(f"\033[92m» {res}\033[0m")
93+
94+
95+
test()

0 commit comments

Comments
 (0)