diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/README.md b/solution/1900-1999/1948.Delete Duplicate Folders in System/README.md index 1096aa5c79333..073d0a88e91b7 100644 --- a/solution/1900-1999/1948.Delete Duplicate Folders in System/README.md +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/README.md @@ -122,153 +122,348 @@ tags: -### 方法一 +### 方法一:字典树 + DFS + +我们可以使用字典树来存储文件夹的结构,字典树的每个节点数据如下: + +- `children`:一个字典,键为子文件夹的名称,值为对应的子节点。 +- `deleted`:一个布尔值,表示该节点是否被标记为待删除。 + +我们将所有路径插入到字典树中,然后使用 DFS 遍历字典树,构建每个子树的字符串表示。对于每个子树,如果它的字符串表示已经存在于一个全局字典中,则将该节点和全局字典中的对应节点都标记为待删除。最后,再次使用 DFS 遍历字典树,将未被标记的节点的路径添加到结果列表中。 #### Python3 ```python -class TrieNode: +class Trie: def __init__(self): - self.children = collections.defaultdict(TrieNode) - self.key = "" - self.deleted = False + self.children: Dict[str, "Trie"] = defaultdict(Trie) + self.deleted: bool = False class Solution: def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: - root = TrieNode() - + root = Trie() for path in paths: - current = root - for folder in path: - current = current.children[folder] - current.key = folder + cur = root + for name in path: + if cur.children[name] is None: + cur.children[name] = Trie() + cur = cur.children[name] - seen = collections.defaultdict(list) + g: Dict[str, Trie] = {} - def dfs(node): + def dfs(node: Trie) -> str: if not node.children: return "" - keys = [] - for key, child in node.children.items(): - serialized = dfs(child) - keys.append(f"{key}({serialized})") - keys.sort() - serialized = "".join(keys) - if len(seen[serialized]) > 0: - for duplicate in seen[serialized]: - duplicate.deleted = True - node.deleted = True - seen[serialized].append(node) - return serialized - - dfs(root) - - result = [] - path = [] - - def collect(node): + subs: List[str] = [] + for name, child in node.children.items(): + subs.append(f"{name}({dfs(child)})") + s = "".join(sorted(subs)) + if s in g: + node.deleted = g[s].deleted = True + else: + g[s] = node + return s + + def dfs2(node: Trie) -> None: if node.deleted: return if path: - result.append(path.copy()) - for key, child in node.children.items(): - path.append(key) - collect(child) + ans.append(path[:]) + for name, child in node.children.items(): + path.append(name) + dfs2(child) path.pop() - collect(root) - return result + dfs(root) + ans: List[List[str]] = [] + path: List[str] = [] + dfs2(root) + return ans ``` #### Java ```java +class Trie { + Map children; + boolean deleted; + + public Trie() { + children = new HashMap<>(); + deleted = false; + } +} + +class Solution { + public List> deleteDuplicateFolder(List> paths) { + Trie root = new Trie(); + for (List path : paths) { + Trie cur = root; + for (String name : path) { + if (!cur.children.containsKey(name)) { + cur.children.put(name, new Trie()); + } + cur = cur.children.get(name); + } + } + + Map g = new HashMap<>(); + var dfs = new Function() { + @Override + public String apply(Trie node) { + if (node.children.isEmpty()) { + return ""; + } + List subs = new ArrayList<>(); + for (var entry : node.children.entrySet()) { + subs.add(entry.getKey() + "(" + apply(entry.getValue()) + ")"); + } + Collections.sort(subs); + String s = String.join("", subs); + if (g.containsKey(s)) { + node.deleted = true; + g.get(s).deleted = true; + } else { + g.put(s, node); + } + return s; + } + }; + + dfs.apply(root); + + List> ans = new ArrayList<>(); + List path = new ArrayList<>(); + + var dfs2 = new Function() { + @Override + public Void apply(Trie node) { + if (node.deleted) { + return null; + } + if (!path.isEmpty()) { + ans.add(new ArrayList<>(path)); + } + for (Map.Entry entry : node.children.entrySet()) { + path.add(entry.getKey()); + apply(entry.getValue()); + path.remove(path.size() - 1); + } + return null; + } + }; + + dfs2.apply(root); + + return ans; + } +} ``` #### C++ ```cpp +class Trie { +public: + unordered_map children; + bool deleted = false; +}; + +class Solution { +public: + vector> deleteDuplicateFolder(vector>& paths) { + Trie* root = new Trie(); + + for (auto& path : paths) { + Trie* cur = root; + for (auto& name : path) { + if (cur->children.find(name) == cur->children.end()) { + cur->children[name] = new Trie(); + } + cur = cur->children[name]; + } + } + + unordered_map g; + + auto dfs = [&](this auto&& dfs, Trie* node) -> string { + if (node->children.empty()) return ""; + + vector subs; + for (auto& child : node->children) { + subs.push_back(child.first + "(" + dfs(child.second) + ")"); + } + sort(subs.begin(), subs.end()); + string s = ""; + for (auto& sub : subs) s += sub; + + if (g.contains(s)) { + node->deleted = true; + g[s]->deleted = true; + } else { + g[s] = node; + } + return s; + }; + + dfs(root); + vector> ans; + vector path; + + auto dfs2 = [&](this auto&& dfs2, Trie* node) -> void { + if (node->deleted) return; + if (!path.empty()) { + ans.push_back(path); + } + for (auto& child : node->children) { + path.push_back(child.first); + dfs2(child.second); + path.pop_back(); + } + }; + + dfs2(root); + + return ans; + } +}; ``` #### Go ```go -type TrieNode struct { - children map[string]*TrieNode - key string - deleted bool +type Trie struct { + children map[string]*Trie + deleted bool } -func deleteDuplicateFolder(paths [][]string) [][]string { - root := &TrieNode{children: make(map[string]*TrieNode)} +func NewTrie() *Trie { + return &Trie{ + children: make(map[string]*Trie), + } +} - for _, path := range paths { - current := root - for _, folder := range path { - if _, ok := current.children[folder]; !ok { - current.children[folder] = &TrieNode{ - children: make(map[string]*TrieNode), - key: folder, - } +func deleteDuplicateFolder(paths [][]string) (ans [][]string) { + root := NewTrie() + for _, path := range paths { + cur := root + for _, name := range path { + if _, exists := cur.children[name]; !exists { + cur.children[name] = NewTrie() + } + cur = cur.children[name] + } + } + + g := make(map[string]*Trie) + + var dfs func(*Trie) string + dfs = func(node *Trie) string { + if len(node.children) == 0 { + return "" + } + var subs []string + for name, child := range node.children { + subs = append(subs, name+"("+dfs(child)+")") + } + sort.Strings(subs) + s := strings.Join(subs, "") + if existingNode, exists := g[s]; exists { + node.deleted = true + existingNode.deleted = true + } else { + g[s] = node + } + return s + } + + var dfs2 func(*Trie, []string) + dfs2 = func(node *Trie, path []string) { + if node.deleted { + return + } + if len(path) > 0 { + ans = append(ans, append([]string{}, path...)) + } + for name, child := range node.children { + dfs2(child, append(path, name)) + } + } + + dfs(root) + dfs2(root, []string{}) + return ans +} +``` + +#### TypeScript + +```ts +function deleteDuplicateFolder(paths: string[][]): string[][] { + class Trie { + children: { [key: string]: Trie } = {}; + deleted: boolean = false; + } + + const root = new Trie(); + + for (const path of paths) { + let cur = root; + for (const name of path) { + if (!cur.children[name]) { + cur.children[name] = new Trie(); } - current = current.children[folder] + cur = cur.children[name]; } } - seen := make(map[string]*TrieNode) - var dfs func(*TrieNode) string - dfs = func(node *TrieNode) string { - if node == nil || len(node.children) == 0 { - return "" - } + const g: { [key: string]: Trie } = {}; - var keys []string - for key, child := range node.children { - serialized := dfs(child) - keys = append(keys, key+"("+serialized+")") + const dfs = (node: Trie): string => { + if (Object.keys(node.children).length === 0) return ''; + + const subs: string[] = []; + for (const [name, child] of Object.entries(node.children)) { + subs.push(`${name}(${dfs(child)})`); } - sort.Strings(keys) - serialized := strings.Join(keys, "") + subs.sort(); + const s = subs.join(''); - if existing, ok := seen[serialized]; ok { - existing.deleted = true - node.deleted = true + if (g[s]) { + node.deleted = true; + g[s].deleted = true; } else { - seen[serialized] = node + g[s] = node; } + return s; + }; - return serialized - } - dfs(root) - - var result [][]string - var path []string - var collect func(*TrieNode) - collect = func(node *TrieNode) { - if node.deleted { - return - } - if len(path) > 0 { - newPath := make([]string, len(path)) - copy(newPath, path) - result = append(result, newPath) + dfs(root); + + const ans: string[][] = []; + const path: string[] = []; + + const dfs2 = (node: Trie): void => { + if (node.deleted) return; + if (path.length > 0) { + ans.push([...path]); } - for key, child := range node.children { - path = append(path, key) - collect(child) - path = path[:len(path)-1] + for (const [name, child] of Object.entries(node.children)) { + path.push(name); + dfs2(child); + path.pop(); } - } - collect(root) + }; - return result -} + dfs2(root); + return ans; +} ``` diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/README_EN.md b/solution/1900-1999/1948.Delete Duplicate Folders in System/README_EN.md index 18e22554d5601..46b0bef223ac5 100644 --- a/solution/1900-1999/1948.Delete Duplicate Folders in System/README_EN.md +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/README_EN.md @@ -101,153 +101,348 @@ Note that the returned array can be in a different order as the order does not m -### Solution 1 +### Solution 1: Trie + DFS + +We can use a trie to store the folder structure, where each node in the trie contains the following data: + +- `children`: A dictionary where the key is the name of the subfolder and the value is the corresponding child node. +- `deleted`: A boolean value indicating whether the node is marked for deletion. + +We insert all paths into the trie, then use DFS to traverse the trie and build a string representation for each subtree. For each subtree, if its string representation already exists in a global dictionary, we mark both the current node and the corresponding node in the global dictionary for deletion. Finally, we use DFS again to traverse the trie and add the paths of unmarked nodes to the result list. #### Python3 ```python -class TrieNode: +class Trie: def __init__(self): - self.children = collections.defaultdict(TrieNode) - self.key = "" - self.deleted = False + self.children: Dict[str, "Trie"] = defaultdict(Trie) + self.deleted: bool = False class Solution: def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: - root = TrieNode() - + root = Trie() for path in paths: - current = root - for folder in path: - current = current.children[folder] - current.key = folder + cur = root + for name in path: + if cur.children[name] is None: + cur.children[name] = Trie() + cur = cur.children[name] - seen = collections.defaultdict(list) + g: Dict[str, Trie] = {} - def dfs(node): + def dfs(node: Trie) -> str: if not node.children: return "" - keys = [] - for key, child in node.children.items(): - serialized = dfs(child) - keys.append(f"{key}({serialized})") - keys.sort() - serialized = "".join(keys) - if len(seen[serialized]) > 0: - for duplicate in seen[serialized]: - duplicate.deleted = True - node.deleted = True - seen[serialized].append(node) - return serialized - - dfs(root) - - result = [] - path = [] - - def collect(node): + subs: List[str] = [] + for name, child in node.children.items(): + subs.append(f"{name}({dfs(child)})") + s = "".join(sorted(subs)) + if s in g: + node.deleted = g[s].deleted = True + else: + g[s] = node + return s + + def dfs2(node: Trie) -> None: if node.deleted: return if path: - result.append(path.copy()) - for key, child in node.children.items(): - path.append(key) - collect(child) + ans.append(path[:]) + for name, child in node.children.items(): + path.append(name) + dfs2(child) path.pop() - collect(root) - return result + dfs(root) + ans: List[List[str]] = [] + path: List[str] = [] + dfs2(root) + return ans ``` #### Java ```java +class Trie { + Map children; + boolean deleted; + + public Trie() { + children = new HashMap<>(); + deleted = false; + } +} + +class Solution { + public List> deleteDuplicateFolder(List> paths) { + Trie root = new Trie(); + for (List path : paths) { + Trie cur = root; + for (String name : path) { + if (!cur.children.containsKey(name)) { + cur.children.put(name, new Trie()); + } + cur = cur.children.get(name); + } + } + + Map g = new HashMap<>(); + var dfs = new Function() { + @Override + public String apply(Trie node) { + if (node.children.isEmpty()) { + return ""; + } + List subs = new ArrayList<>(); + for (var entry : node.children.entrySet()) { + subs.add(entry.getKey() + "(" + apply(entry.getValue()) + ")"); + } + Collections.sort(subs); + String s = String.join("", subs); + if (g.containsKey(s)) { + node.deleted = true; + g.get(s).deleted = true; + } else { + g.put(s, node); + } + return s; + } + }; + + dfs.apply(root); + + List> ans = new ArrayList<>(); + List path = new ArrayList<>(); + + var dfs2 = new Function() { + @Override + public Void apply(Trie node) { + if (node.deleted) { + return null; + } + if (!path.isEmpty()) { + ans.add(new ArrayList<>(path)); + } + for (Map.Entry entry : node.children.entrySet()) { + path.add(entry.getKey()); + apply(entry.getValue()); + path.remove(path.size() - 1); + } + return null; + } + }; + + dfs2.apply(root); + + return ans; + } +} ``` #### C++ ```cpp +class Trie { +public: + unordered_map children; + bool deleted = false; +}; + +class Solution { +public: + vector> deleteDuplicateFolder(vector>& paths) { + Trie* root = new Trie(); + + for (auto& path : paths) { + Trie* cur = root; + for (auto& name : path) { + if (cur->children.find(name) == cur->children.end()) { + cur->children[name] = new Trie(); + } + cur = cur->children[name]; + } + } + + unordered_map g; + + auto dfs = [&](this auto&& dfs, Trie* node) -> string { + if (node->children.empty()) return ""; + + vector subs; + for (auto& child : node->children) { + subs.push_back(child.first + "(" + dfs(child.second) + ")"); + } + sort(subs.begin(), subs.end()); + string s = ""; + for (auto& sub : subs) s += sub; + + if (g.contains(s)) { + node->deleted = true; + g[s]->deleted = true; + } else { + g[s] = node; + } + return s; + }; + + dfs(root); + vector> ans; + vector path; + + auto dfs2 = [&](this auto&& dfs2, Trie* node) -> void { + if (node->deleted) return; + if (!path.empty()) { + ans.push_back(path); + } + for (auto& child : node->children) { + path.push_back(child.first); + dfs2(child.second); + path.pop_back(); + } + }; + + dfs2(root); + + return ans; + } +}; ``` #### Go ```go -type TrieNode struct { - children map[string]*TrieNode - key string - deleted bool +type Trie struct { + children map[string]*Trie + deleted bool } -func deleteDuplicateFolder(paths [][]string) [][]string { - root := &TrieNode{children: make(map[string]*TrieNode)} +func NewTrie() *Trie { + return &Trie{ + children: make(map[string]*Trie), + } +} - for _, path := range paths { - current := root - for _, folder := range path { - if _, ok := current.children[folder]; !ok { - current.children[folder] = &TrieNode{ - children: make(map[string]*TrieNode), - key: folder, - } +func deleteDuplicateFolder(paths [][]string) (ans [][]string) { + root := NewTrie() + for _, path := range paths { + cur := root + for _, name := range path { + if _, exists := cur.children[name]; !exists { + cur.children[name] = NewTrie() + } + cur = cur.children[name] + } + } + + g := make(map[string]*Trie) + + var dfs func(*Trie) string + dfs = func(node *Trie) string { + if len(node.children) == 0 { + return "" + } + var subs []string + for name, child := range node.children { + subs = append(subs, name+"("+dfs(child)+")") + } + sort.Strings(subs) + s := strings.Join(subs, "") + if existingNode, exists := g[s]; exists { + node.deleted = true + existingNode.deleted = true + } else { + g[s] = node + } + return s + } + + var dfs2 func(*Trie, []string) + dfs2 = func(node *Trie, path []string) { + if node.deleted { + return + } + if len(path) > 0 { + ans = append(ans, append([]string{}, path...)) + } + for name, child := range node.children { + dfs2(child, append(path, name)) + } + } + + dfs(root) + dfs2(root, []string{}) + return ans +} +``` + +#### TypeScript + +```ts +function deleteDuplicateFolder(paths: string[][]): string[][] { + class Trie { + children: { [key: string]: Trie } = {}; + deleted: boolean = false; + } + + const root = new Trie(); + + for (const path of paths) { + let cur = root; + for (const name of path) { + if (!cur.children[name]) { + cur.children[name] = new Trie(); } - current = current.children[folder] + cur = cur.children[name]; } } - seen := make(map[string]*TrieNode) - var dfs func(*TrieNode) string - dfs = func(node *TrieNode) string { - if node == nil || len(node.children) == 0 { - return "" - } + const g: { [key: string]: Trie } = {}; - var keys []string - for key, child := range node.children { - serialized := dfs(child) - keys = append(keys, key+"("+serialized+")") + const dfs = (node: Trie): string => { + if (Object.keys(node.children).length === 0) return ''; + + const subs: string[] = []; + for (const [name, child] of Object.entries(node.children)) { + subs.push(`${name}(${dfs(child)})`); } - sort.Strings(keys) - serialized := strings.Join(keys, "") + subs.sort(); + const s = subs.join(''); - if existing, ok := seen[serialized]; ok { - existing.deleted = true - node.deleted = true + if (g[s]) { + node.deleted = true; + g[s].deleted = true; } else { - seen[serialized] = node + g[s] = node; } + return s; + }; - return serialized - } - dfs(root) - - var result [][]string - var path []string - var collect func(*TrieNode) - collect = func(node *TrieNode) { - if node.deleted { - return - } - if len(path) > 0 { - newPath := make([]string, len(path)) - copy(newPath, path) - result = append(result, newPath) + dfs(root); + + const ans: string[][] = []; + const path: string[] = []; + + const dfs2 = (node: Trie): void => { + if (node.deleted) return; + if (path.length > 0) { + ans.push([...path]); } - for key, child := range node.children { - path = append(path, key) - collect(child) - path = path[:len(path)-1] + for (const [name, child] of Object.entries(node.children)) { + path.push(name); + dfs2(child); + path.pop(); } - } - collect(root) + }; - return result -} + dfs2(root); + return ans; +} ``` diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.cpp b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.cpp new file mode 100644 index 0000000000000..3f7b140bc9353 --- /dev/null +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.cpp @@ -0,0 +1,65 @@ +class Trie { +public: + unordered_map children; + bool deleted = false; +}; + +class Solution { +public: + vector> deleteDuplicateFolder(vector>& paths) { + Trie* root = new Trie(); + + for (auto& path : paths) { + Trie* cur = root; + for (auto& name : path) { + if (cur->children.find(name) == cur->children.end()) { + cur->children[name] = new Trie(); + } + cur = cur->children[name]; + } + } + + unordered_map g; + + auto dfs = [&](this auto&& dfs, Trie* node) -> string { + if (node->children.empty()) return ""; + + vector subs; + for (auto& child : node->children) { + subs.push_back(child.first + "(" + dfs(child.second) + ")"); + } + sort(subs.begin(), subs.end()); + string s = ""; + for (auto& sub : subs) s += sub; + + if (g.contains(s)) { + node->deleted = true; + g[s]->deleted = true; + } else { + g[s] = node; + } + return s; + }; + + dfs(root); + + vector> ans; + vector path; + + auto dfs2 = [&](this auto&& dfs2, Trie* node) -> void { + if (node->deleted) return; + if (!path.empty()) { + ans.push_back(path); + } + for (auto& child : node->children) { + path.push_back(child.first); + dfs2(child.second); + path.pop_back(); + } + }; + + dfs2(root); + + return ans; + } +}; diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go index b83b9846d2f7e..2b61f3796a413 100644 --- a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go @@ -1,70 +1,62 @@ -type TrieNode struct { - children map[string]*TrieNode - key string - deleted bool +type Trie struct { + children map[string]*Trie + deleted bool } -func deleteDuplicateFolder(paths [][]string) [][]string { - root := &TrieNode{children: make(map[string]*TrieNode)} - - for _, path := range paths { - current := root - for _, folder := range path { - if _, ok := current.children[folder]; !ok { - current.children[folder] = &TrieNode{ - children: make(map[string]*TrieNode), - key: folder, - } - } - current = current.children[folder] - } - } - - seen := make(map[string]*TrieNode) - var dfs func(*TrieNode) string - dfs = func(node *TrieNode) string { - if node == nil || len(node.children) == 0 { - return "" - } - - var keys []string - for key, child := range node.children { - serialized := dfs(child) - keys = append(keys, key+"("+serialized+")") - } - sort.Strings(keys) - serialized := strings.Join(keys, "") - - if existing, ok := seen[serialized]; ok { - existing.deleted = true - node.deleted = true - } else { - seen[serialized] = node - } - - return serialized - } - dfs(root) - - var result [][]string - var path []string - var collect func(*TrieNode) - collect = func(node *TrieNode) { - if node.deleted { - return - } - if len(path) > 0 { - newPath := make([]string, len(path)) - copy(newPath, path) - result = append(result, newPath) - } - for key, child := range node.children { - path = append(path, key) - collect(child) - path = path[:len(path)-1] - } - } - collect(root) +func NewTrie() *Trie { + return &Trie{ + children: make(map[string]*Trie), + } +} - return result +func deleteDuplicateFolder(paths [][]string) (ans [][]string) { + root := NewTrie() + for _, path := range paths { + cur := root + for _, name := range path { + if _, exists := cur.children[name]; !exists { + cur.children[name] = NewTrie() + } + cur = cur.children[name] + } + } + + g := make(map[string]*Trie) + + var dfs func(*Trie) string + dfs = func(node *Trie) string { + if len(node.children) == 0 { + return "" + } + var subs []string + for name, child := range node.children { + subs = append(subs, name+"("+dfs(child)+")") + } + sort.Strings(subs) + s := strings.Join(subs, "") + if existingNode, exists := g[s]; exists { + node.deleted = true + existingNode.deleted = true + } else { + g[s] = node + } + return s + } + + var dfs2 func(*Trie, []string) + dfs2 = func(node *Trie, path []string) { + if node.deleted { + return + } + if len(path) > 0 { + ans = append(ans, append([]string{}, path...)) + } + for name, child := range node.children { + dfs2(child, append(path, name)) + } + } + + dfs(root) + dfs2(root, []string{}) + return ans } diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.java b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.java new file mode 100644 index 0000000000000..cd5912d34b40c --- /dev/null +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.java @@ -0,0 +1,75 @@ +class Trie { + Map children; + boolean deleted; + + public Trie() { + children = new HashMap<>(); + deleted = false; + } +} + +class Solution { + public List> deleteDuplicateFolder(List> paths) { + Trie root = new Trie(); + for (List path : paths) { + Trie cur = root; + for (String name : path) { + if (!cur.children.containsKey(name)) { + cur.children.put(name, new Trie()); + } + cur = cur.children.get(name); + } + } + + Map g = new HashMap<>(); + + var dfs = new Function() { + @Override + public String apply(Trie node) { + if (node.children.isEmpty()) { + return ""; + } + List subs = new ArrayList<>(); + for (var entry : node.children.entrySet()) { + subs.add(entry.getKey() + "(" + apply(entry.getValue()) + ")"); + } + Collections.sort(subs); + String s = String.join("", subs); + if (g.containsKey(s)) { + node.deleted = true; + g.get(s).deleted = true; + } else { + g.put(s, node); + } + return s; + } + }; + + dfs.apply(root); + + List> ans = new ArrayList<>(); + List path = new ArrayList<>(); + + var dfs2 = new Function() { + @Override + public Void apply(Trie node) { + if (node.deleted) { + return null; + } + if (!path.isEmpty()) { + ans.add(new ArrayList<>(path)); + } + for (Map.Entry entry : node.children.entrySet()) { + path.add(entry.getKey()); + apply(entry.getValue()); + path.remove(path.size() - 1); + } + return null; + } + }; + + dfs2.apply(root); + + return ans; + } +} diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py index 57dc4c7e37c21..9031f263762d1 100644 --- a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py @@ -1,52 +1,46 @@ -class TrieNode: +class Trie: def __init__(self): - self.children = collections.defaultdict(TrieNode) - self.key = "" - self.deleted = False + self.children: Dict[str, "Trie"] = defaultdict(Trie) + self.deleted: bool = False class Solution: def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: - root = TrieNode() - + root = Trie() for path in paths: - current = root - for folder in path: - current = current.children[folder] - current.key = folder + cur = root + for name in path: + if cur.children[name] is None: + cur.children[name] = Trie() + cur = cur.children[name] - seen = collections.defaultdict(list) + g: Dict[str, Trie] = {} - def dfs(node): + def dfs(node: Trie) -> str: if not node.children: return "" - keys = [] - for key, child in node.children.items(): - serialized = dfs(child) - keys.append(f"{key}({serialized})") - keys.sort() - serialized = "".join(keys) - if len(seen[serialized]) > 0: - for duplicate in seen[serialized]: - duplicate.deleted = True - node.deleted = True - seen[serialized].append(node) - return serialized - - dfs(root) - - result = [] - path = [] - - def collect(node): + subs: List[str] = [] + for name, child in node.children.items(): + subs.append(f"{name}({dfs(child)})") + s = "".join(sorted(subs)) + if s in g: + node.deleted = g[s].deleted = True + else: + g[s] = node + return s + + def dfs2(node: Trie) -> None: if node.deleted: return if path: - result.append(path.copy()) - for key, child in node.children.items(): - path.append(key) - collect(child) + ans.append(path[:]) + for name, child in node.children.items(): + path.append(name) + dfs2(child) path.pop() - collect(root) - return result + dfs(root) + ans: List[List[str]] = [] + path: List[str] = [] + dfs2(root) + return ans diff --git a/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.ts b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.ts new file mode 100644 index 0000000000000..10096fe3014d4 --- /dev/null +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.ts @@ -0,0 +1,60 @@ +function deleteDuplicateFolder(paths: string[][]): string[][] { + class Trie { + children: { [key: string]: Trie } = {}; + deleted: boolean = false; + } + + const root = new Trie(); + + for (const path of paths) { + let cur = root; + for (const name of path) { + if (!cur.children[name]) { + cur.children[name] = new Trie(); + } + cur = cur.children[name]; + } + } + + const g: { [key: string]: Trie } = {}; + + const dfs = (node: Trie): string => { + if (Object.keys(node.children).length === 0) return ''; + + const subs: string[] = []; + for (const [name, child] of Object.entries(node.children)) { + subs.push(`${name}(${dfs(child)})`); + } + subs.sort(); + const s = subs.join(''); + + if (g[s]) { + node.deleted = true; + g[s].deleted = true; + } else { + g[s] = node; + } + return s; + }; + + dfs(root); + + const ans: string[][] = []; + const path: string[] = []; + + const dfs2 = (node: Trie): void => { + if (node.deleted) return; + if (path.length > 0) { + ans.push([...path]); + } + for (const [name, child] of Object.entries(node.children)) { + path.push(name); + dfs2(child); + path.pop(); + } + }; + + dfs2(root); + + return ans; +}