Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given a 0-indexed array of strings words. Each string consists of lowercase English letters only. No letter occurs more than once in any string of words.
Two strings s1 and s2 are said to be connected if the set of letters of s2 can be obtained from the set of letters of s1 by any one of the following operations:
s1.s1.s1 with any letter, including itself.The array words can be divided into one or more non-intersecting groups. A string belongs to a group if any one of the following is true:
Note that the strings in words should be grouped in such a manner that a string belonging to a group cannot be connected to a string present in any other group. It can be proved that such an arrangement is always unique.
Return an array ans of size 2 where:
ans[0] is the maximum number of groups words can be divided into, andans[1] is the size of the largest group.Example 1:
Input: words = ["a","b","ab","cde"] Output: [2,3] Explanation: - words[0] can be used to obtain words[1] (by replacing 'a' with 'b'), and words[2] (by adding 'b'). So words[0] is connected to words[1] and words[2]. - words[1] can be used to obtain words[0] (by replacing 'b' with 'a'), and words[2] (by adding 'a'). So words[1] is connected to words[0] and words[2]. - words[2] can be used to obtain words[0] (by deleting 'b'), and words[1] (by deleting 'a'). So words[2] is connected to words[0] and words[1]. - words[3] is not connected to any string in words. Thus, words can be divided into 2 groups ["a","b","ab"] and ["cde"]. The size of the largest group is 3.
Example 2:
Input: words = ["a","ab","abc"] Output: [1,3] Explanation: - words[0] is connected to words[1]. - words[1] is connected to words[0] and words[2]. - words[2] is connected to words[1]. Since all strings are connected to each other, they should be grouped together. Thus, the size of the largest group is 3.
Constraints:
1 <= words.length <= 2 * 1041 <= words[i].length <= 26words[i] consists of lowercase English letters only.words[i].Problem summary: You are given a 0-indexed array of strings words. Each string consists of lowercase English letters only. No letter occurs more than once in any string of words. Two strings s1 and s2 are said to be connected if the set of letters of s2 can be obtained from the set of letters of s1 by any one of the following operations: Adding exactly one letter to the set of the letters of s1. Deleting exactly one letter from the set of the letters of s1. Replacing exactly one letter from the set of the letters of s1 with any letter, including itself. The array words can be divided into one or more non-intersecting groups. A string belongs to a group if any one of the following is true: It is connected to at least one other string of the group. It is the only string present in the group. Note that the strings in words should be grouped in such a manner that a string belonging to a group cannot be
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Bit Manipulation · Union-Find
["a","b","ab","cde"]
["a","ab","abc"]
word-ladder-ii)similar-string-groups)largest-component-size-by-common-factor)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2157: Groups of Strings
class Solution {
private Map<Integer, Integer> p;
private Map<Integer, Integer> size;
private int mx;
private int n;
public int[] groupStrings(String[] words) {
p = new HashMap<>();
size = new HashMap<>();
n = words.length;
mx = 0;
for (String word : words) {
int x = 0;
for (char c : word.toCharArray()) {
x |= 1 << (c - 'a');
}
p.put(x, x);
size.put(x, size.getOrDefault(x, 0) + 1);
mx = Math.max(mx, size.get(x));
if (size.get(x) > 1) {
--n;
}
}
for (int x : p.keySet()) {
for (int i = 0; i < 26; ++i) {
union(x, x ^ (1 << i));
if (((x >> i) & 1) != 0) {
for (int j = 0; j < 26; ++j) {
if (((x >> j) & 1) == 0) {
union(x, x ^ (1 << i) | (1 << j));
}
}
}
}
}
return new int[] {n, mx};
}
private int find(int x) {
if (p.get(x) != x) {
p.put(x, find(p.get(x)));
}
return p.get(x);
}
private void union(int a, int b) {
if (!p.containsKey(b)) {
return;
}
int pa = find(a), pb = find(b);
if (pa == pb) {
return;
}
p.put(pa, pb);
size.put(pb, size.get(pb) + size.get(pa));
mx = Math.max(mx, size.get(pb));
--n;
}
}
// Accepted solution for LeetCode #2157: Groups of Strings
func groupStrings(words []string) []int {
p := map[int]int{}
size := map[int]int{}
mx, n := 0, len(words)
var find func(int) int
find = func(x int) int {
if p[x] != x {
p[x] = find(p[x])
}
return p[x]
}
union := func(a, b int) {
if _, ok := p[b]; !ok {
return
}
pa, pb := find(a), find(b)
if pa == pb {
return
}
p[pa] = pb
size[pb] += size[pa]
mx = max(mx, size[pb])
n--
}
for _, word := range words {
x := 0
for _, c := range word {
x |= 1 << (c - 'a')
}
p[x] = x
size[x]++
mx = max(mx, size[x])
if size[x] > 1 {
n--
}
}
for x := range p {
for i := 0; i < 26; i++ {
union(x, x^(1<<i))
if ((x >> i) & 1) != 0 {
for j := 0; j < 26; j++ {
if ((x >> j) & 1) == 0 {
union(x, x^(1<<i)|(1<<j))
}
}
}
}
}
return []int{n, mx}
}
# Accepted solution for LeetCode #2157: Groups of Strings
class Solution:
def groupStrings(self, words: List[str]) -> List[int]:
def find(x):
if p[x] != x:
p[x] = find(p[x])
return p[x]
def union(a, b):
nonlocal mx, n
if b not in p:
return
pa, pb = find(a), find(b)
if pa == pb:
return
p[pa] = pb
size[pb] += size[pa]
mx = max(mx, size[pb])
n -= 1
p = {}
size = Counter()
n = len(words)
mx = 0
for word in words:
x = 0
for c in word:
x |= 1 << (ord(c) - ord('a'))
p[x] = x
size[x] += 1
mx = max(mx, size[x])
if size[x] > 1:
n -= 1
for x in p.keys():
for i in range(26):
union(x, x ^ (1 << i))
if (x >> i) & 1:
for j in range(26):
if ((x >> j) & 1) == 0:
union(x, x ^ (1 << i) | (1 << j))
return [n, mx]
// Accepted solution for LeetCode #2157: Groups of Strings
/**
* [2157] Groups of Strings
*
* You are given a 0-indexed array of strings words. Each string consists of lowercase English letters only. No letter occurs more than once in any string of words.
* Two strings s1 and s2 are said to be connected if the set of letters of s2 can be obtained from the set of letters of s1 by any one of the following operations:
*
* Adding exactly one letter to the set of the letters of s1.
* Deleting exactly one letter from the set of the letters of s1.
* Replacing exactly one letter from the set of the letters of s1 with any letter, including itself.
*
* The array words can be divided into one or more non-intersecting groups. A string belongs to a group if any one of the following is true:
*
* It is connected to at least one other string of the group.
* It is the only string present in the group.
*
* Note that the strings in words should be grouped in such a manner that a string belonging to a group cannot be connected to a string present in any other group. It can be proved that such an arrangement is always unique.
* Return an array ans of size 2 where:
*
* ans[0] is the maximum number of groups words can be divided into, and
* ans[1] is the size of the largest group.
*
*
* Example 1:
*
* Input: words = ["a","b","ab","cde"]
* Output: [2,3]
* Explanation:
* - words[0] can be used to obtain words[1] (by replacing 'a' with 'b'), and words[2] (by adding 'b'). So words[0] is connected to words[1] and words[2].
* - words[1] can be used to obtain words[0] (by replacing 'b' with 'a'), and words[2] (by adding 'a'). So words[1] is connected to words[0] and words[2].
* - words[2] can be used to obtain words[0] (by deleting 'b'), and words[1] (by deleting 'a'). So words[2] is connected to words[0] and words[1].
* - words[3] is not connected to any string in words.
* Thus, words can be divided into 2 groups ["a","b","ab"] and ["cde"]. The size of the largest group is 3.
*
* Example 2:
*
* Input: words = ["a","ab","abc"]
* Output: [1,3]
* Explanation:
* - words[0] is connected to words[1].
* - words[1] is connected to words[0] and words[2].
* - words[2] is connected to words[1].
* Since all strings are connected to each other, they should be grouped together.
* Thus, the size of the largest group is 3.
*
*
* Constraints:
*
* 1 <= words.length <= 2 * 10^4
* 1 <= words[i].length <= 26
* words[i] consists of lowercase English letters only.
* No letter occurs more than once in words[i].
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/groups-of-strings/
// discuss: https://leetcode.com/problems/groups-of-strings/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
pub fn group_strings(words: Vec<String>) -> Vec<i32> {
vec![]
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_2157_example_1() {
let words = vec_string!["a", "b", "ab", "cde"];
let result = vec![2, 3];
assert_eq!(Solution::group_strings(words), result);
}
#[test]
#[ignore]
fn test_2157_example_2() {
let words = vec_string!["a", "ab", "abc"];
let result = vec![1, 3];
assert_eq!(Solution::group_strings(words), result);
}
}
// Accepted solution for LeetCode #2157: Groups of Strings
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #2157: Groups of Strings
// class Solution {
// private Map<Integer, Integer> p;
// private Map<Integer, Integer> size;
// private int mx;
// private int n;
//
// public int[] groupStrings(String[] words) {
// p = new HashMap<>();
// size = new HashMap<>();
// n = words.length;
// mx = 0;
// for (String word : words) {
// int x = 0;
// for (char c : word.toCharArray()) {
// x |= 1 << (c - 'a');
// }
// p.put(x, x);
// size.put(x, size.getOrDefault(x, 0) + 1);
// mx = Math.max(mx, size.get(x));
// if (size.get(x) > 1) {
// --n;
// }
// }
// for (int x : p.keySet()) {
// for (int i = 0; i < 26; ++i) {
// union(x, x ^ (1 << i));
// if (((x >> i) & 1) != 0) {
// for (int j = 0; j < 26; ++j) {
// if (((x >> j) & 1) == 0) {
// union(x, x ^ (1 << i) | (1 << j));
// }
// }
// }
// }
// }
// return new int[] {n, mx};
// }
//
// private int find(int x) {
// if (p.get(x) != x) {
// p.put(x, find(p.get(x)));
// }
// return p.get(x);
// }
//
// private void union(int a, int b) {
// if (!p.containsKey(b)) {
// return;
// }
// int pa = find(a), pb = find(b);
// if (pa == pb) {
// return;
// }
// p.put(pa, pb);
// size.put(pb, size.get(pb) + size.get(pa));
// mx = Math.max(mx, size.get(pb));
// --n;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Sort the array in O(n log n), then scan for the missing or unique element by comparing adjacent pairs. Sorting requires O(n) auxiliary space (or O(1) with in-place sort but O(n log n) time remains). The sort step dominates.
Bitwise operations (AND, OR, XOR, shifts) are O(1) per operation on fixed-width integers. A single pass through the input with bit operations gives O(n) time. The key insight: XOR of a number with itself is 0, which eliminates duplicates without extra space.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.