Formatted question description: https://leetcode.ca/all/1948.html

1948. Delete Duplicate Folders in System

Level

Hard

Description

Due to a bug, there are many duplicate folders in a file system. You are given a 2D array paths, where paths[i] is an array representing an absolute path to the i-th folder in the file system.

  • For example, ["one", "two", "three"] represents the path "/one/two/three".

Two folders (not necessarily on the same level) are identical if they contain the same non-empty set of identical subfolders and underlying subfolder structure. The folders do not need to be at the root level to be identical. If two or more folders are identical, then mark the folders as well as all their subfolders.

  • For example, folders "/a" and "/b" in the file structure below are identical. They (as well as their subfolders) should all be marked:
    • /a
    • /a/x
    • /a/x/y
    • /a/z
    • /b
    • /b/x
    • /b/x/y
    • /b/z
  • However, if the file structure also included the path "/b/w", then the folders "/a" and "/b" would not be identical. Note that "/a/x" and "/b/x" would still be considered identical even with the added folder.

Once all the identical folders and their subfolders have been marked, the file system will delete all of them. The file system only runs the deletion once, so any folders that become identical after the initial deletion are not deleted.

Return the 2D array ans containing the paths of the remaining folders after deleting all the marked folders. The paths may be returned in any order.

Example 1:

Image text

Input: paths = [[“a”],[“c”],[“d”],[“a”,”b”],[“c”,”b”],[“d”,”a”]]

Output: [[“d”],[“d”,”a”]]

Explanation: The file structure is as shown.

Folders “/a” and “/c” (and their subfolders) are marked for deletion because they both contain an empty folder named “b”.

Example 2:

Image text

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.

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.

Example 3:

Image text

Input: paths = [[“a”,”b”],[“c”,”d”],[“c”],[“a”]]

Output: [[“c”],[“c”,”d”],[“a”],[“a”,”b”]]

Explanation: All folders are unique in the file system.

Note that the returned array can be in a different order as the order does not matter.

Example 4:

Image text

Input: paths = [[“a”],[“a”,”x”],[“a”,”x”,”y”],[“a”,”z”],[“b”],[“b”,”x”],[“b”,”x”,”y”],[“b”,”z”]]

Output: []

Explanation: The file structure is as shown.

Folders “/a/x” and “/b/x” (and their subfolders) are marked for deletion because they both contain an empty folder named “y”.

Folders “/a” and “/b” (and their subfolders) are marked for deletion because they both contain an empty folder “z” and the folder “x” described above.

Example 5:

Image text

Input: paths = [[“a”],[“a”,”x”],[“a”,”x”,”y”],[“a”,”z”],[“b”],[“b”,”x”],[“b”,”x”,”y”],[“b”,”z”],[“b”,”w”]]

Output: [[“b”],[“b”,”w”],[“b”,”z”],[“a”],[“a”,”z”]]

Explanation: This has the same structure as the previous example, except with the added “/b/w”.

Folders “/a/x” and “/b/x” are still marked, but “/a” and “/b” are no longer marked because “/b” has the empty folder named “w” and “/a” does not.

Note that “/a/z” and “/b/z” are not marked because the set of identical subfolders must be non-empty, but these folders are empty.

Constraints:

  • 1 <= paths.length <= 2 * 10^4
  • 1 <= paths[i].length <= 500
  • 1 <= paths[i][j].length <= 10
  • 1 <= sum(paths[i][j].length) <= 2 * 10^5
  • path[i][j] consists of lowercase English letters.
  • No two paths lead to the same folder.
  • For any folder not at the root level, its parent folder will also be in the input.

Solution

Use a trie to represent the file system. In the trie, the root is "/" and each of the remaining nodes represent a folder. Then encode the folders and use a hash map to store each encoding’s number of occurrences. If an encoding occurs more than once, then it should be deleted. Finally, return the remaining folders.

class Solution {
    public List<List<String>> deleteDuplicateFolder(List<List<String>> paths) {
        TrieNode root = new TrieNode();
        for (List<String> path : paths) {
            TrieNode curr = root;
            for (String node : path) {
                if (!curr.children.containsKey(node))
                    curr.children.put(node, new TrieNode());
                curr = curr.children.get(node);
            }
        }
        Map<String, Integer> freq = new HashMap<String, Integer>();
        construct(root, freq);
        List<List<String>> remaining = new ArrayList<List<String>>();
        List<String> path = new ArrayList<String>();
        operate(root, freq, remaining, path);
        return remaining;
    }

    public void construct(TrieNode node, Map<String, Integer> freq) {
        if (node.children.isEmpty())
            return;
        List<String> list = new ArrayList<String>();
        for (Map.Entry<String, TrieNode> entry : node.children.entrySet()) {
            String folder = entry.getKey();
            TrieNode child = entry.getValue();
            construct(child, freq);
            list.add(folder + "(" + child.serial.toString() + ")");
        }
        Collections.sort(list);
        for (String str : list)
            node.serial.append(str);
        String serialStr = node.serial.toString();
        freq.put(serialStr, freq.getOrDefault(serialStr, 0) + 1);
    }

    public void operate(TrieNode node, Map<String, Integer> freq, List<List<String>> remaining, List<String> path) {
        if (freq.getOrDefault(node.serial.toString(), 0) > 1)
            return;
        if (!path.isEmpty())
            remaining.add(new ArrayList<String>(path));
        for (Map.Entry<String, TrieNode> entry : node.children.entrySet()) {
            String folder = entry.getKey();
            TrieNode child = entry.getValue();
            path.add(folder);
            operate(child, freq, remaining, path);
            path.remove(path.size() - 1);
        }
    }
}

class TrieNode {
    StringBuffer serial;
    Map<String, TrieNode> children;

    public TrieNode() {
        serial = new StringBuffer();
        children = new HashMap<String, TrieNode>();
    }
}

All Problems

All Solutions