Loading problems...
TL;DR: The optimal solution treats words as nodes in a graph and uses the Union-Find (Disjoint Set Union) data structure to efficiently group transitively similar words into connected components, allowing similarity checks.
In LeetCode 737: Sentence Similarity II, we are given two sentences (arrays of words) and a list of similar word pairs. The core challenge is that similarity is transitive: if "great" is similar to "fine", and "fine" is similar to "good", then "great" is also similar to "good". We must determine if the two sentences are equivalent, meaning the word at every index in the first sentence is similar to the word at index in the second sentence.
This problem is a classic example of determining connectivity in a graph where dynamic updates or static connectivity queries are required.
We will implement the solution using a coordinate-compressed or map-based DSU since the inputs are strings, not integers 0 to .
parent where parent[word] stores the parent of word. If a word is not in the map, it is its own parent.parent[x] = x for all x.similarPairs. For each pair (w1, w2), find the root of w1 and the root of w2. If they are different, set parent[root1] = root2.find(w) with path compression. This recursively looks up the parent until parent[w] == w. On the way back up the recursion stack, update every node's parent to the root to optimize future lookups.i, check if find(sentence1[i]) == find(sentence2[i]). If any pair has different roots, return false.sentence1 and sentence2 have the same length. If not, return false immediately.parents.[u, v] in similarPairs.union(u, v). This involves calling find(u) and find(v).u or v are not present in the map, initialize them pointing to themselves.u to the root of v.i from 0 to n-1.w1 = sentence1[i] and w2 = sentence2[i].w1 == w2, continue (trivially similar).root1 = find(w1) and root2 = find(w2).root1 != root2, the words belong to different components. Return false.false, return true.The algorithm relies on the invariant of the Disjoint Set data structure: two elements share the same representative root if and only if they have been merged through a sequence of union operations. Since the union operations correspond exactly to the similarity pairs provided, and similarity is transitive, sharing a root is mathematically equivalent to being transitively similar. By verifying that every corresponding pair of words in the sentences shares a root, we strictly satisfy the problem's condition for sentence similarity.
cpp#include <vector> #include <string> #include <unordered_map> using namespace std; class Solution { private: // Map to store the parent of each word unordered_map<string, string> parent; // Find function with Path Compression string find(string s) { if (parent.find(s) == parent.end()) { parent[s] = s; // Initialize lazily } if (parent[s] != s) { parent[s] = find(parent[s]); // Path compression } return parent[s]; } // Union function to merge two sets void unite(string s1, string s2) { string root1 = find(s1); string root2 = find(s2); if (root1 != root2) { parent[root1] = root2; } } public: bool areSentencesSimilarTwo(vector<string>& sentence1, vector<string>& sentence2, vector<vector<string>>& similarPairs) { if (sentence1.size() != sentence2.size()) { return false; } // Step 1: Build the DSU structure from similarPairs for (const auto& pair : similarPairs) { unite(pair[0], pair[1]); } // Step 2: Check similarity for each word pair in the sentences for (int i = 0; i < sentence1.size(); ++i) { string w1 = sentence1[i]; string w2 = sentence2[i]; // If words are identical, they are similar. // If they are different, check if they belong to the same component. if (w1 != w2 && find(w1) != find(w2)) { return false; } } return true; } };
javaimport java.util.*; class Solution { // Map to store parent pointers private Map<String, String> parent = new HashMap<>(); // Find with Path Compression private String find(String s) { if (!parent.containsKey(s)) { parent.put(s, s); } if (!s.equals(parent.get(s))) { parent.put(s, find(parent.get(s))); } return parent.get(s); } // Union operation private void union(String s1, String s2) { String root1 = find(s1); String root2 = find(s2); if (!root1.equals(root2)) { parent.put(root1, root2); } } public boolean areSentencesSimilarTwo(String[] sentence1, String[] sentence2, List<List<String>> similarPairs) { if (sentence1.length != sentence2.length) { return false; } // Build graph for (List<String> pair : similarPairs) { union(pair.get(0), pair.get(1)); } // Check sentences for (int i = 0; i < sentence1.length; i++) { String w1 = sentence1[i]; String w2 = sentence2[i]; if (!w1.equals(w2) && !find(w1).equals(find(w2))) { return false; } } return true; } }
pythonclass Solution: def areSentencesSimilarTwo(self, sentence1: list[str], sentence2: list[str], similarPairs: list[list[str]]) -> bool: if len(sentence1) != len(sentence2): return False parent = {} # Find with Path Compression def find(word): if word not in parent: parent[word] = word if parent[word] != word: parent[word] = find(parent[word]) return parent[word] # Union operation def union(word1, word2): root1 = find(word1) root2 = find(word2) if root1 != root2: parent[root1] = root2 # Build DSU structure for w1, w2 in similarPairs: union(w1, w2) # Verify sentences for w1, w2 in zip(sentence1, sentence2): if w1 != w2 and find(w1) != find(w2): return False return True
Let be the length of the sentences, be the number of similar pairs, and be the maximum length of a word (effectively constant, ).
The Graph - Union-Find pattern is highly reusable for problems involving connectivity, grouping, or equivalence relations.
find(u) == find(v) before union).sentence1 has 1000 words, you might traverse the similarPairs graph 1000 times.parent map. Each word takes up to space.similarPairs?
sentence1 or sentence2 exists in the similarPairs list.find function must lazily initialize unseen words as their own parents.find operations instead of .