[Leetcode week race 159] 1233 to delete subfolder

1233 Remove Sub-Folders from the Filesystem to delete subfolders

Problem Description

You are a system administrator, the hands have a list of folders folder, your task is to remove all the list of subfolders and to any order to return the rest of the folder.

We define "sub-folders":

  • If the folder is  folder[i] a folder in another file  folder[j] under, it  folder[i] is  folder[j] a sub-folder.

Folder "path" is a string of one or more of the following format formed in series:

  • / Followed by one or more lower-case letters.

For example, /leetcode and  /leetcode/problems they are effective path, and the empty string  / not.

Example 1:

Input: Folder = [ "/ A", "/ A / B", "/ C / D", "/ C / D / E", "/ C / F"]
Output: [ "/ A", "/ c / d "," / c / f "]
explains: " / a / B / "is" / a "sub-folder, and" / c / d / e "is" / c / d "subfiles folder.

Example 2:

Input: Folder = [ "/ A", "/ a / b / c", "/ A / B / D"]
Output: [ "/ A"]
Explanation: the folder "/ a / b / c" and " / a / b / d / "will be deleted, because they are" / a "sub-folder.

Example 3:

输入: folder = ["/a/b/c","/a/b/d","/a/b/ca"]
输出: ["/a/b/c","/a/b/ca","/a/b/d"]

prompt:

  • 1 <= folder.length <= 4 * 10^4
  • 2 <= folder[i].length <= 100
  • folder[i] It contains only lowercase letters and /
  • folder[i] Character always / start
  • Each folder name is unique

Thinking

  • Read title
    in the path list, select the parent path, the path of the Father is the prefix sub path

Violence Act (sort + simple pruning)

Pairwise alignments path, there may be three cases: a first parent path is a path, the first path is a sub-path, two paths are not related to
(A, B) --> (A->B)/(B->A)/(A<-!->B)
tag all sub-paths, avoid them all together in the final parent paths

  • This approach is easy to timeouts, you can first be sorted , and then screening the first character matched two paths for comparison, thereby reducing the scale than to reduce the amount of computation
    + + Sort of violence than simple pruning

Looking law (sorting + collection filter)

We may note sorting after the start of the path is the parent path , and ranked in the following may be relatively large path his son
and then one by one to each path /path prefix than the existing set path (this can optimization)

  1. No comparison to the parent itself is a path into the collection
  2. Comparison to the exclusion
    + + Sorting to find the law a set of screening

+ DFS affix tree

Affixes to build a directory tree named node, the depth of the leaf node traversal recent
Affix tree

Code

Violence Act

class Solution {
    public List<String> removeSubfolders(String[] folder) {
        int len = folder.length;
        if (len <= 1) {
            return Collections.singletonList(folder[0]);
        }

        Arrays.sort(folder);
        boolean[] delete = new boolean[len];
        // 每两条路径进行比对 子路径删除(标记删除)
        for (int i = 0; i < len; i++) {
            for (int j = i + 1; j < len; j++) {
                // 简单的剪枝 判断第二个字符是否相等作为 比较两路径的前提
                if (!delete[i] && !delete[j] && (folder[i].charAt(1) == folder[j].charAt(1))) {
                    // 路径之间相互比较
                    if (aIsBParent(folder[i], folder[j])) {
                        delete[j] = true;
                    } else if (aIsBParent(folder[j], folder[i])) {
                        delete[i] = true;
                    }
                }
            }
        }

        // 没被标记删除的路径都是父路径
        List<String> ans = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            if (!delete[i]) {
                ans.add(folder[i]);
            }
        }

        return ans;
    }

    private boolean aIsBParent(String a, String b) {
        // 如果a的路径比b的还长 则a必定不是b的父路径
        if (a.length() >= b.length()) {
            return false;
        }

        // 前缀是否相同 [/a/b] -> [/a/b]/c
        return b.charAt(a.length()) == '/' && a.equals(b.substring(0, a.length()));
    }
}

Find the law

class Solution {
    public List<String> removeSubfolders(String[] folder) {
        int len = folder.length;
        Arrays.sort(folder);
        Set<String> parents = new HashSet<>(len);

        // 指定一条路径 切分其前缀路径比对已知父路径
        for (String f : folder) {
            boolean curIsParent = true;
            int fLen = f.length();
            // 因为是排序的 开始的路径必定是父路径(set存储)
            for (int i = 1; i < fLen; i++) {
                // 切分前缀路径 判断是否是已知父路径
                if (f.charAt(i) == '/' && parents.contains(f.substring(0, i))) {
                    curIsParent = false;
                }
            }

            if (curIsParent) {
                parents.add(f);
            }
        }

        return new ArrayList<>(parents);
    }
}

Affix tree

class Solution {
    private class Node {
        String name;
        Map<String, Node> nexts;

        Node() {
            // 结尾标记 普通节点name="" 叶子节点name=path
            name = "";
            nexts = new HashMap<>();
        }

        @Override
        /**
            * debug时 可以显示内容
            */
        public String toString() {
            return "[" + name + "={" + nexts + "}]";
        }
    }

    public List<String> removeSubfolders(String[] folder) {
        // 建立词缀树
        Node root = new Node();
        for (String f : folder) {
            String[] fs = f.split("/");
            // 每次从根节点开始
            Node cur = root;
            for (String s : fs) {
                if (!"".equals(s)) {
                    // 如果没有 则新建节点
                    if (!cur.nexts.containsKey(s)) {
                        cur.nexts.put(s, new Node());
                    }
                    // 切换到下一节点(键值为s)
                    cur = cur.nexts.get(s);
                }
            }
            // 每条路径结尾处 使用name=f做标记
            cur.name = f;
        }

        List<String> ans = new ArrayList<>();
        // 深度优先遍历
        dfs(root, ans);

        return ans;
    }

    private void dfs(Node root, List<String> ans) {
        // 最优先找到的叶子节点 就无需继续往下找了
        if (!"".equals(root.name)) {
            ans.add(root.name);
            return;
        }

        // 遍历当前节点的所有下一节点
        Set<String> set = root.nexts.keySet();
        for (String s : set) {
            // a -> b| -> /a/b
            // a -> b| -> c -> /a/b/c [X]
            // a -> c -> d -> /a/c/d
            dfs(root.nexts.get(s), ans);
        }
    }
}

Resources

159 of the world's top tournament games Week

Guess you like

Origin www.cnblogs.com/slowbirdoflsh/p/11779139.html