@@ -122,32 +122,348 @@ tags:
122122
123123<!-- solution:start -->
124124
125- ### 方法一
125+ ### 方法一:字典树 + DFS
126+
127+ 我们可以使用字典树来存储文件夹的结构,字典树的每个节点数据如下:
128+
129+ - ` children ` :一个字典,键为子文件夹的名称,值为对应的子节点。
130+ - ` deleted ` :一个布尔值,表示该节点是否被标记为待删除。
131+
132+ 我们将所有路径插入到字典树中,然后使用 DFS 遍历字典树,构建每个子树的字符串表示。对于每个子树,如果它的字符串表示已经存在于一个全局字典中,则将该节点和全局字典中的对应节点都标记为待删除。最后,再次使用 DFS 遍历字典树,将未被标记的节点的路径添加到结果列表中。
126133
127134<!-- tabs:start -->
128135
129136#### Python3
130137
131138``` python
132-
139+ class Trie :
140+ def __init__ (self ):
141+ self .children: Dict[str , " Trie" ] = defaultdict(Trie)
142+ self .deleted: bool = False
143+
144+
145+ class Solution :
146+ def deleteDuplicateFolder (self , paths : List[List[str ]]) -> List[List[str ]]:
147+ root = Trie()
148+ for path in paths:
149+ cur = root
150+ for name in path:
151+ if cur.children[name] is None :
152+ cur.children[name] = Trie()
153+ cur = cur.children[name]
154+
155+ g: Dict[str , Trie] = {}
156+
157+ def dfs (node : Trie) -> str :
158+ if not node.children:
159+ return " "
160+ subs: List[str ] = []
161+ for name, child in node.children.items():
162+ subs.append(f " { name} ( { dfs(child)} ) " )
163+ s = " " .join(sorted (subs))
164+ if s in g:
165+ node.deleted = g[s].deleted = True
166+ else :
167+ g[s] = node
168+ return s
169+
170+ def dfs2 (node : Trie) -> None :
171+ if node.deleted:
172+ return
173+ if path:
174+ ans.append(path[:])
175+ for name, child in node.children.items():
176+ path.append(name)
177+ dfs2(child)
178+ path.pop()
179+
180+ dfs(root)
181+ ans: List[List[str ]] = []
182+ path: List[str ] = []
183+ dfs2(root)
184+ return ans
133185```
134186
135187#### Java
136188
137189``` java
138-
190+ class Trie {
191+ Map<String , Trie > children;
192+ boolean deleted;
193+
194+ public Trie () {
195+ children = new HashMap<> ();
196+ deleted = false ;
197+ }
198+ }
199+
200+ class Solution {
201+ public List<List<String > > deleteDuplicateFolder (List<List<String > > paths ) {
202+ Trie root = new Trie ();
203+ for (List<String > path : paths) {
204+ Trie cur = root;
205+ for (String name : path) {
206+ if (! cur. children. containsKey(name)) {
207+ cur. children. put(name, new Trie ());
208+ }
209+ cur = cur. children. get(name);
210+ }
211+ }
212+
213+ Map<String , Trie > g = new HashMap<> ();
214+
215+ var dfs = new Function<Trie , String > () {
216+ @Override
217+ public String apply (Trie node ) {
218+ if (node. children. isEmpty()) {
219+ return " " ;
220+ }
221+ List<String > subs = new ArrayList<> ();
222+ for (var entry : node. children. entrySet()) {
223+ subs. add(entry. getKey() + " (" + apply(entry. getValue()) + " )" );
224+ }
225+ Collections . sort(subs);
226+ String s = String . join(" " , subs);
227+ if (g. containsKey(s)) {
228+ node. deleted = true ;
229+ g. get(s). deleted = true ;
230+ } else {
231+ g. put(s, node);
232+ }
233+ return s;
234+ }
235+ };
236+
237+ dfs. apply(root);
238+
239+ List<List<String > > ans = new ArrayList<> ();
240+ List<String > path = new ArrayList<> ();
241+
242+ var dfs2 = new Function<Trie , Void > () {
243+ @Override
244+ public Void apply (Trie node ) {
245+ if (node. deleted) {
246+ return null ;
247+ }
248+ if (! path. isEmpty()) {
249+ ans. add(new ArrayList<> (path));
250+ }
251+ for (Map . Entry<String , Trie > entry : node. children. entrySet()) {
252+ path. add(entry. getKey());
253+ apply(entry. getValue());
254+ path. remove(path. size() - 1 );
255+ }
256+ return null ;
257+ }
258+ };
259+
260+ dfs2. apply(root);
261+
262+ return ans;
263+ }
264+ }
139265```
140266
141267#### C++
142268
143269``` cpp
144-
270+ class Trie {
271+ public:
272+ unordered_map<string, Trie* > children;
273+ bool deleted = false;
274+ };
275+
276+ class Solution {
277+ public:
278+ vector<vector<string >> deleteDuplicateFolder(vector<vector<string >>& paths) {
279+ Trie* root = new Trie();
280+
281+ for (auto& path : paths) {
282+ Trie* cur = root;
283+ for (auto& name : path) {
284+ if (cur->children.find(name) == cur->children.end()) {
285+ cur->children[name] = new Trie();
286+ }
287+ cur = cur->children[name];
288+ }
289+ }
290+
291+ unordered_map<string, Trie*> g;
292+
293+ auto dfs = [&](this auto && dfs, Trie* node) -> string {
294+ if (node->children.empty()) return "";
295+
296+ vector<string> subs;
297+ for (auto& child : node->children) {
298+ subs.push_back(child.first + "(" + dfs(child.second) + ")");
299+ }
300+ sort (subs.begin(), subs.end());
301+ string s = "";
302+ for (auto& sub : subs) s += sub;
303+
304+ if (g.contains(s)) {
305+ node->deleted = true;
306+ g[s]->deleted = true;
307+ } else {
308+ g[s] = node;
309+ }
310+ return s;
311+ };
312+
313+ dfs (root);
314+
315+ vector<vector<string>> ans;
316+ vector<string> path;
317+
318+ auto dfs2 = [&](this auto&& dfs2, Trie* node) -> void {
319+ if (node->deleted) return;
320+ if (!path.empty()) {
321+ ans.push_back(path);
322+ }
323+ for (auto& child : node->children) {
324+ path.push_back(child.first);
325+ dfs2 (child.second);
326+ path.pop_back();
327+ }
328+ };
329+
330+ dfs2 (root);
331+
332+ return ans;
333+ }
334+ };
145335```
146336
147337#### Go
148338
149339``` go
340+ type Trie struct {
341+ children map [string ]*Trie
342+ deleted bool
343+ }
344+
345+ func NewTrie () *Trie {
346+ return &Trie{
347+ children: make (map [string ]*Trie),
348+ }
349+ }
350+
351+ func deleteDuplicateFolder (paths [][]string ) (ans [][]string ) {
352+ root := NewTrie ()
353+ for _ , path := range paths {
354+ cur := root
355+ for _ , name := range path {
356+ if _ , exists := cur.children [name]; !exists {
357+ cur.children [name] = NewTrie ()
358+ }
359+ cur = cur.children [name]
360+ }
361+ }
362+
363+ g := make (map [string ]*Trie)
364+
365+ var dfs func (*Trie) string
366+ dfs = func (node *Trie) string {
367+ if len (node.children ) == 0 {
368+ return " "
369+ }
370+ var subs []string
371+ for name , child := range node.children {
372+ subs = append (subs, name+" (" +dfs (child)+" )" )
373+ }
374+ sort.Strings (subs)
375+ s := strings.Join (subs, " " )
376+ if existingNode , exists := g[s]; exists {
377+ node.deleted = true
378+ existingNode.deleted = true
379+ } else {
380+ g[s] = node
381+ }
382+ return s
383+ }
384+
385+ var dfs2 func (*Trie, []string )
386+ dfs2 = func (node *Trie, path []string ) {
387+ if node.deleted {
388+ return
389+ }
390+ if len (path) > 0 {
391+ ans = append (ans, append ([]string {}, path...))
392+ }
393+ for name , child := range node.children {
394+ dfs2 (child, append (path, name))
395+ }
396+ }
397+
398+ dfs (root)
399+ dfs2 (root, []string {})
400+ return ans
401+ }
402+ ```
150403
404+ #### TypeScript
405+
406+ ``` ts
407+ function deleteDuplicateFolder(paths : string [][]): string [][] {
408+ class Trie {
409+ children: { [key : string ]: Trie } = {};
410+ deleted: boolean = false ;
411+ }
412+
413+ const root = new Trie ();
414+
415+ for (const path of paths ) {
416+ let cur = root ;
417+ for (const name of path ) {
418+ if (! cur .children [name ]) {
419+ cur .children [name ] = new Trie ();
420+ }
421+ cur = cur .children [name ];
422+ }
423+ }
424+
425+ const g: { [key : string ]: Trie } = {};
426+
427+ const dfs = (node : Trie ): string => {
428+ if (Object .keys (node .children ).length === 0 ) return ' ' ;
429+
430+ const subs: string [] = [];
431+ for (const [name, child] of Object .entries (node .children )) {
432+ subs .push (` ${name }(${dfs (child )}) ` );
433+ }
434+ subs .sort ();
435+ const s = subs .join (' ' );
436+
437+ if (g [s ]) {
438+ node .deleted = true ;
439+ g [s ].deleted = true ;
440+ } else {
441+ g [s ] = node ;
442+ }
443+ return s ;
444+ };
445+
446+ dfs (root );
447+
448+ const ans: string [][] = [];
449+ const path: string [] = [];
450+
451+ const dfs2 = (node : Trie ): void => {
452+ if (node .deleted ) return ;
453+ if (path .length > 0 ) {
454+ ans .push ([... path ]);
455+ }
456+ for (const [name, child] of Object .entries (node .children )) {
457+ path .push (name );
458+ dfs2 (child );
459+ path .pop ();
460+ }
461+ };
462+
463+ dfs2 (root );
464+
465+ return ans ;
466+ }
151467```
152468
153469<!-- tabs:end -->
0 commit comments