class Solution:
def findSmallestRegion(self, regions, region1, region2):
parent = {}
for region in regions:
for child in region[1:]:
parent[child] = region[0]
ancestors = set()
while region1:
ancestors.add(region1)
region1 = parent.get(region1)
while region2:
if region2 in ancestors:
return region2
region2 = parent.get(region2)
class Solution {
public:
string findSmallestRegion(vector<vector<string>>& regions, string region1, string region2) {
unordered_map<string, string> parent;
for (const auto& region : regions) {
for (int i = 1; i < region.size(); ++i) {
parent[region[i]] = region[0];
}
}
unordered_set<string> ancestors;
while (!region1.empty()) {
ancestors.insert(region1);
region1 = parent.count(region1) ? parent[region1] : "";
}
while (!region2.empty()) {
if (ancestors.count(region2)) return region2;
region2 = parent.count(region2) ? parent[region2] : "";
}
return "";
}
};
class Solution {
public String findSmallestRegion(List<List<String>> regions, String region1, String region2) {
Map<String, String> parent = new HashMap<>();
for (List<String> region : regions) {
for (int i = 1; i < region.size(); i++) {
parent.put(region.get(i), region.get(0));
}
}
Set<String> ancestors = new HashSet<>();
while (region1 != null) {
ancestors.add(region1);
region1 = parent.get(region1);
}
while (region2 != null) {
if (ancestors.contains(region2)) return region2;
region2 = parent.get(region2);
}
return "";
}
}
var findSmallestRegion = function(regions, region1, region2) {
const parent = {};
for (const region of regions) {
for (let i = 1; i < region.length; i++) {
parent[region[i]] = region[0];
}
}
const ancestors = new Set();
while (region1) {
ancestors.add(region1);
region1 = parent[region1];
}
while (region2) {
if (ancestors.has(region2)) return region2;
region2 = parent[region2];
}
return "";
};
You are given a list of regions, where each region is represented as a list of strings. The first element of each list is the parent region, followed by its direct subregions. Given two region names, region1
and region2
, your task is to find their smallest common region — the lowest region in the hierarchy that contains both region1
and region2
.
Key constraints:
Example Input:
regions = [["Earth", "North America", "South America"], ["North America", "United States", "Canada"], ["United States", "New York", "Boston"], ["Canada", "Ontario", "Quebec"], ["South America", "Brazil"]]
region1 = "Quebec", region2 = "New York"
Output: "North America"
At first glance, this problem resembles finding the lowest common ancestor (LCA) in a tree structure. Each region can be seen as a node, with parent-child relationships defined by the input. To find the smallest common region for two given regions, we need to trace their paths up to the root and find where those paths intersect.
A brute-force approach might involve building the entire tree, then for each query, traversing from both regions up to the root and comparing their paths. However, this can be simplified by using a hash map to store parent relationships, making ancestor lookups efficient. The key insight is that we do not need to build the entire tree structure; we only need to know each region's immediate parent.
By storing the path from one region to the root in a set, we can then walk up from the other region and return the first ancestor found in the set — this is their lowest common ancestor.
regions
list.region1
, walk up the parent map, adding each region to a set.region2
, walk up the parent map.region1
's ancestors, return it — this is the smallest common region.
Let's use the sample input:
regions = [["Earth", "North America", "South America"], ["North America", "United States", "Canada"], ["United States", "New York", "Boston"], ["Canada", "Ontario", "Quebec"], ["South America", "Brazil"]]
region1 = "Quebec", region2 = "New York"
Thus, the smallest common region is "North America".
This efficiency is possible because each lookup and ancestor check is O(1) due to hash maps and sets.
The solution to the Smallest Common Region problem leverages the tree structure of the input data. By mapping each region to its parent, we can efficiently trace the ancestry of any region. The intersection of ancestor paths yields the smallest region containing both target regions. This approach is both simple and efficient, using hash maps and sets for quick lookups, and elegantly reduces a potentially complex tree traversal problem to a series of parent lookups and set operations.