题目考查了DFS,Trie。还蛮精妙的。反正面试我遇到这题的话就跪给面试官了。
思路是首先建立Trie,顺着文件夹的路径,建立全部的Trie结构。然后开始对每条路径序列化。这里的讲究是,在设置TrieNode的时候,设置其是否需要被cut。在序列化的时候使用。当序列化时,按照题目的要求,类似b-c 和 d-c这样的文件结构,b-c、d-c都要cut掉,也就是说,见到c,记录其父文件夹(节点),后再次遇到c时,连同第二次遇到的c及其父节点d都要删除,所以。设置meet,类似于BFS中的meet set。但是这里是要看序列化时是否出现重复的序列。出现则两个序列化后一样的节点和其父节点都要删除。在代码中的序列化时,比如a-b-c,然后a还有另外一条分支,a-d-c。在序列化a时,继续调用serial函数,开始处理b,那对于b这层循环,会继续调用serial(b),在这层,得到©(End),这个序列化没见过,记录到meet中,其父节点是b,后产生(b)(©(End)),父节点为a,但是所有节点的cut都没被改写为True。顺序执行到d,后serail(d),得到©(End),次序列化结果出现过,此时首先将目前序列化的附节点的cut改写为True,也就是d。然后将meet dict中的©(End)序列已经指向的父节点b的cut改写为True。类似的操作就可以将全部一样的文件夹的父节点设置为cut,等再次遍历Trie的时候,见到cut为True时就不再往下dfs。每一层,将现阶段的文件夹路径记录到最终res中,并揪住此节点的字节点继续往下dfs。
class TrieNode:
def __init__(self):
self.cut = False
self.children = collections.defaultdict(TrieNode)
class Solution:
def deleteDuplicateFolder(self, paths: List[List[str]]) -> List[List[str]]:
paths = sorted(paths)
root = TrieNode()
for path in paths:
curnode = root
for letter in path:
if letter not in curnode.children:
curnode.children[letter] = TrieNode()
curnode = curnode.children[letter]
meet = collections.defaultdict()
def serial(root):
if len(root.children) == 0:
return "End"
res = ""
for child in root.children:
res += '(' + child + ')' + '(' + serial(root.children[child]) + ')'
if res in meet:
root.cut = True
meet[res].cut = True
else:
meet[res] = root
return res
serial(root)
curpath = []
res = []
def dfs(root, curpath, res):
if root.cut:return
if curpath:
res.append(curpath)
for child in root.children:
dfs(root.children[child], curpath + [child], res)
dfs(root, curpath, res)
return res