From d3c3c60e24b2048dbd8c88541f83a7c0bc9b8c2a Mon Sep 17 00:00:00 2001 From: "rom.spiridonov" Date: Sun, 20 Jul 2025 15:18:21 +0300 Subject: [PATCH] feat: add solution for deleting duplicate folders in a file system --- .../README.md | 123 ++++++++++++++++- .../README_EN.md | 125 +++++++++++++++++- .../Solution.go | 70 ++++++++++ .../Solution.py | 52 ++++++++ 4 files changed, 367 insertions(+), 3 deletions(-) create mode 100644 solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go create mode 100644 solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py 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 d3255436fb79c..1096aa5c79333 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 @@ -129,7 +129,58 @@ tags: #### Python3 ```python - +class TrieNode: + def __init__(self): + self.children = collections.defaultdict(TrieNode) + self.key = "" + self.deleted = False + + +class Solution: + def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: + root = TrieNode() + + for path in paths: + current = root + for folder in path: + current = current.children[folder] + current.key = folder + + seen = collections.defaultdict(list) + + def dfs(node): + 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): + if node.deleted: + return + if path: + result.append(path.copy()) + for key, child in node.children.items(): + path.append(key) + collect(child) + path.pop() + + collect(root) + return result ``` #### Java @@ -147,6 +198,76 @@ tags: #### Go ```go +type TrieNode struct { + children map[string]*TrieNode + key string + 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) + + return result +} ``` 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 01fa7d10a03c7..18e22554d5601 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 @@ -68,7 +68,7 @@ folder named "b".
 Input: paths = [["a"],["c"],["a","b"],["c","b"],["a","b","x"],["a","b","x","y"],["w"],["w","y"]]
 Output: [["c"],["c","b"],["a"],["a","b"]]
-Explanation: The file structure is as shown. 
+Explanation: The file structure is as shown.
 Folders "/a/b/x" and "/w" (and their subfolders) are marked for deletion because they both contain an empty folder named "y".
 Note that folders "/a" and "/c" are identical after the deletion, but they are not deleted because they were not marked beforehand.
 
@@ -108,7 +108,58 @@ Note that the returned array can be in a different order as the order does not m #### Python3 ```python - +class TrieNode: + def __init__(self): + self.children = collections.defaultdict(TrieNode) + self.key = "" + self.deleted = False + + +class Solution: + def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: + root = TrieNode() + + for path in paths: + current = root + for folder in path: + current = current.children[folder] + current.key = folder + + seen = collections.defaultdict(list) + + def dfs(node): + 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): + if node.deleted: + return + if path: + result.append(path.copy()) + for key, child in node.children.items(): + path.append(key) + collect(child) + path.pop() + + collect(root) + return result ``` #### Java @@ -126,6 +177,76 @@ Note that the returned array can be in a different order as the order does not m #### Go ```go +type TrieNode struct { + children map[string]*TrieNode + key string + 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) + + return result +} ``` 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 new file mode 100644 index 0000000000000..b83b9846d2f7e --- /dev/null +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.go @@ -0,0 +1,70 @@ +type TrieNode struct { + children map[string]*TrieNode + key string + 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) + + return result +} 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 new file mode 100644 index 0000000000000..57dc4c7e37c21 --- /dev/null +++ b/solution/1900-1999/1948.Delete Duplicate Folders in System/Solution.py @@ -0,0 +1,52 @@ +class TrieNode: + def __init__(self): + self.children = collections.defaultdict(TrieNode) + self.key = "" + self.deleted = False + + +class Solution: + def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]: + root = TrieNode() + + for path in paths: + current = root + for folder in path: + current = current.children[folder] + current.key = folder + + seen = collections.defaultdict(list) + + def dfs(node): + 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): + if node.deleted: + return + if path: + result.append(path.copy()) + for key, child in node.children.items(): + path.append(key) + collect(child) + path.pop() + + collect(root) + return result