Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d2ea5b5

Browse files
committed
Add solution #2157
1 parent 1fbf170 commit d2ea5b5

File tree

2 files changed

+142
-1
lines changed

2 files changed

+142
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 1,782 LeetCode solutions in JavaScript
1+
# 1,783 LeetCode solutions in JavaScript
22

33
[https://leetcodejavascript.com](https://leetcodejavascript.com)
44

@@ -1652,6 +1652,7 @@
16521652
2154|[Keep Multiplying Found Values by Two](./solutions/2154-keep-multiplying-found-values-by-two.js)|Easy|
16531653
2155|[All Divisions With the Highest Score of a Binary Array](./solutions/2155-all-divisions-with-the-highest-score-of-a-binary-array.js)|Medium|
16541654
2156|[Find Substring With Given Hash Value](./solutions/2156-find-substring-with-given-hash-value.js)|Hard|
1655+
2157|[Groups of Strings](./solutions/2157-groups-of-strings.js)|Hard|
16551656
2161|[Partition Array According to Given Pivot](./solutions/2161-partition-array-according-to-given-pivot.js)|Medium|
16561657
2176|[Count Equal and Divisible Pairs in an Array](./solutions/2176-count-equal-and-divisible-pairs-in-an-array.js)|Easy|
16571658
2179|[Count Good Triplets in an Array](./solutions/2179-count-good-triplets-in-an-array.js)|Hard|

solutions/2157-groups-of-strings.js

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* 2157. Groups of Strings
3+
* https://leetcode.com/problems/groups-of-strings/
4+
* Difficulty: Hard
5+
*
6+
* You are given a 0-indexed array of strings words. Each string consists of lowercase English
7+
* letters only. No letter occurs more than once in any string of words.
8+
*
9+
* Two strings s1 and s2 are said to be connected if the set of letters of s2 can be obtained
10+
* from the set of letters of s1 by any one of the following operations:
11+
* - Adding exactly one letter to the set of the letters of s1.
12+
* - Deleting exactly one letter from the set of the letters of s1.
13+
* - Replacing exactly one letter from the set of the letters of s1 with any letter, including
14+
* itself.
15+
*
16+
* The array words can be divided into one or more non-intersecting groups. A string belongs
17+
* to a group if any one of the following is true:
18+
* - It is connected to at least one other string of the group.
19+
* - It is the only string present in the group.
20+
*
21+
* Note that the strings in words should be grouped in such a manner that a string belonging
22+
* to a group cannot be connected to a string present in any other group. It can be proved
23+
* that such an arrangement is always unique.
24+
*
25+
* Return an array ans of size 2 where:
26+
* - ans[0] is the maximum number of groups words can be divided into, and
27+
* - ans[1] is the size of the largest group.
28+
*/
29+
30+
/**
31+
* @param {string[]} words
32+
* @return {number[]}
33+
*/
34+
var groupStrings = function(words) {
35+
const n = words.length;
36+
const masks = words.map(word => {
37+
let mask = 0;
38+
for (let i = 0; i < word.length; i++) {
39+
mask |= (1 << (word.charCodeAt(i) - 'a'.charCodeAt(0)));
40+
}
41+
return mask;
42+
});
43+
const uf = new UnionFind(n);
44+
const maskToIndex = new Map();
45+
for (let i = 0; i < n; i++) {
46+
if (maskToIndex.has(masks[i])) {
47+
uf.union(maskToIndex.get(masks[i]), i);
48+
} else {
49+
maskToIndex.set(masks[i], i);
50+
}
51+
}
52+
const processed = new Set();
53+
54+
for (let i = 0; i < n; i++) {
55+
const mask = masks[i];
56+
if (processed.has(mask)) continue;
57+
processed.add(mask);
58+
59+
for (let bit = 0; bit < 26; bit++) {
60+
if ((mask & (1 << bit)) === 0) {
61+
const newMask = mask | (1 << bit);
62+
if (maskToIndex.has(newMask)) {
63+
uf.union(i, maskToIndex.get(newMask));
64+
}
65+
}
66+
}
67+
68+
for (let bit = 0; bit < 26; bit++) {
69+
if ((mask & (1 << bit)) !== 0) {
70+
const newMask = mask & ~(1 << bit);
71+
if (maskToIndex.has(newMask)) {
72+
uf.union(i, maskToIndex.get(newMask));
73+
}
74+
}
75+
}
76+
77+
for (let remove = 0; remove < 26; remove++) {
78+
if ((mask & (1 << remove)) !== 0) {
79+
for (let add = 0; add < 26; add++) {
80+
if ((mask & (1 << add)) === 0) {
81+
const newMask = (mask & ~(1 << remove)) | (1 << add);
82+
if (maskToIndex.has(newMask)) {
83+
uf.union(i, maskToIndex.get(newMask));
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
91+
return [uf.count, uf.getMaxSize()];
92+
};
93+
94+
class UnionFind {
95+
constructor(n) {
96+
this.parent = Array(n).fill().map((_, i) => i);
97+
this.rank = Array(n).fill(0);
98+
this.count = n;
99+
this.sizes = Array(n).fill(1);
100+
}
101+
102+
find(x) {
103+
if (this.parent[x] !== x) {
104+
this.parent[x] = this.find(this.parent[x]);
105+
}
106+
return this.parent[x];
107+
}
108+
109+
union(x, y) {
110+
const rootX = this.find(x);
111+
const rootY = this.find(y);
112+
113+
if (rootX === rootY) return false;
114+
115+
if (this.rank[rootX] < this.rank[rootY]) {
116+
this.parent[rootX] = rootY;
117+
this.sizes[rootY] += this.sizes[rootX];
118+
} else if (this.rank[rootX] > this.rank[rootY]) {
119+
this.parent[rootY] = rootX;
120+
this.sizes[rootX] += this.sizes[rootY];
121+
} else {
122+
this.parent[rootY] = rootX;
123+
this.rank[rootX]++;
124+
this.sizes[rootX] += this.sizes[rootY];
125+
}
126+
127+
this.count--;
128+
return true;
129+
}
130+
131+
getMaxSize() {
132+
let maxSize = 0;
133+
for (let i = 0; i < this.parent.length; i++) {
134+
if (this.parent[i] === i) {
135+
maxSize = Math.max(maxSize, this.sizes[i]);
136+
}
137+
}
138+
return maxSize;
139+
}
140+
}

0 commit comments

Comments
 (0)