Given a list of folders folder, return the folders after removing all sub-folders in those folders. You may return the answer in any order.
If a folder[i] is located within another folder[j], it is called a sub-folder of it. A sub-folder of folder[j] must start with folder[j], followed by a "/". For example, "/a/b" is a sub-folder of "/a", but "/b" is not a sub-folder of "/a/b/c".
The format of a path is one or more concatenated strings of the form: '/' followed by one or more lowercase English letters.
- For example,
"/leetcode"and"/leetcode/problems"are valid paths while an empty string and"/"are not.
solutions
trie + break early on insertion
We break early on insertion and return False if during insertion we hit an ending node, meaning that a parent folder has already been seen.
Note that we need to first sort lexicographically for this to work.
class TrieNode:
def __init__(self, val=None, is_end=False):
self.val = val
self.is_end = False
self.children = {}
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, parts: list[str]):
cur = self.root
for part in parts:
if part in cur.children:
if cur.children[part].is_end:
return False
cur = cur.children[part]
else: # new TrieNode
new_node = TrieNode(part)
cur.children[part] = (new_node)
cur = new_node
cur.is_end = True
return True
def removeSubfolders(self, folder: List[str]) -> List[str]:
res = []
t = Trie()
folder.sort()
for path in folder:
parts = path.split("/")
if t.insert(parts):
res.append(path)
return restrie + break early on collection/traversal
We can insert all paths into the trie, and then DFS the trie, but break whenever we hit a node marked as is_end, as every further traversal would be a sub-folder.
class TrieNode:
def __init__(self, val=None, is_end=False):
self.val = val
self.is_end = False
self.children = {}
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, parts: list[str]):
cur = self.root
for part in parts:
if part not in cur.children:
new_node = TrieNode(part)
cur.children[part] = new_node
cur = cur.children[part]
cur.is_end = True
def collect(self, root: TrieNode):
parents = []
acc = []
def _helper(node):
if not node:
return
# first ending node is parent
# all further traversal would give subfolders
if node.is_end:
parents.append("/".join(acc))
return
for child_key in node.children:
acc.append(node.children[child_key].val)
_helper(node.children[child_key])
acc.pop()
_helper(root)
return parents
def removeSubfolders(self, folder: List[str]) -> List[str]:
res = []
t = Trie()
for path in folder:
parts = path.split("/")
t.insert(parts)
return t.collect(t.root)