1233. 删除子文件夹

艾因LeetCode题解大约 3 分钟

1233. 删除子文件夹

题目描述

你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹。

如果文件夹 folder[i] 位于另一个文件夹 folder[j] 下,那么 folder[i] 就是 folder[j] 的 子文件夹 。

文件夹的「路径」是由一个或多个按以下格式串联形成的字符串:'/' 后跟一个或者多个小写英文字母。

  • 例如,"/leetcode" 和 "/leetcode/problems" 都是有效的路径,而空字符串和 "/" 不是。

示例 1:

输入:folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]
输出:["/a","/c/d","/c/f"]
解释:"/a/b" 是 "/a" 的子文件夹,而 "/c/d/e" 是 "/c/d" 的子文件夹。

示例 2:

输入:folder = ["/a","/a/b/c","/a/b/d"]
输出:["/a"]
解释:文件夹 "/a/b/c" 和 "/a/b/d" 都会被删除,因为它们都是 "/a" 的子文件夹。

示例 3:

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

提示:

  • 1 <= folder.length <= 4 * 104
  • 2 <= folder[i].length <= 100
  • folder[i] 只包含小写字母和 '/'
  • folder[i] 总是以字符 '/' 起始
  • 每个文件夹名都是 唯一 的

思路

时间复杂的分析

根据提示中的数据,folder的数量在104数量级,每个folder[i]小于100,则需要思考时间复杂为O(n)的解法,

n为folder.length*folder[i].length。

解题思路

朴素的解法是两层循环,即对每一个folder[i]进行0到folder.length的遍历,寻找其前缀是否存在,存在就跳过,不存在则将其加入结果中。

因为是求前缀是否存在,我们可以通过排序来减少全部遍历。

将folder进行字典序的排序,我们可以发现,如果当前字符串包含了前一个字符串且前缀和前一个字符串相同,那我们就可以把当前字符串去掉,继续下一个循环。如果不相同,我们则将当前字符串加入答案,并且更新前一个字符串为当前字符串并继续循环。

另外一种思路,求是否存在前缀,字典树是比较合适的方法。时间复杂度也是合适的。

数据结构选择

如果采用排序的方式,则只需要一个List作为答案进行返回,不需要额外的数据结构。

如果采用字典树的方式,则需要进行字典树的生成。

题解


//排序的方式
class Solution {
    public List<String> removeSubfolders(String[] folder) {
        Arrays.sort(folder);                                //排序
        int n=folder.length;
        List<String> ans=new ArrayList<>();
        int idx=0;
        ans.add(folder[0]);
        for(int i=1;i<n;i++){
            int xn=folder[i].length(),x3=xn-1;
            int yn=folder[idx].length();
            while(x3>0){
                if(folder[i].charAt(x3)=='/'){
                    break;
                }
                x3--;
            }                                                  //获取0到最后一个‘/’字符间的子串
            if(folder[i].substring(0,x3).contans(folder[idx]) &&
            yn<xn && folder[idx].equals(folder[i].substring(0,yn))  //判断子串是否包含前一个字符串,同时前缀相同
            ){
                continue;
            }
            else{
                ans.add(folder[i]);
                idx=i;
            }
        }
        return ans;
    }
}


// 字典树的方式,来自力扣题解

class Solution {
    public List<String> removeSubfolders(String[] folder) {
        Trie root = new Trie();
        for (int i = 0; i < folder.length; ++i) {
            List<String> path = split(folder[i]);
            Trie cur = root;
            for (String name : path) {
                cur.children.putIfAbsent(name, new Trie());
                cur = cur.children.get(name);
            }
            cur.ref = i;
        }

        List<String> ans = new ArrayList<String>();
        dfs(folder, ans, root);
        return ans;
    }

    public List<String> split(String s) {
        List<String> ret = new ArrayList<String>();
        StringBuilder cur = new StringBuilder();
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (ch == '/') {
                ret.add(cur.toString());
                cur.setLength(0);
            } else {
                cur.append(ch);
            }
        }
        ret.add(cur.toString());
        return ret;
    }

    public void dfs(String[] folder, List<String> ans, Trie cur) {
        if (cur.ref != -1) {
            ans.add(folder[cur.ref]);
            return;
        }
        for (Trie child : cur.children.values()) {
            dfs(folder, ans, child);
        }
    }
}

class Trie {
    int ref;
    Map<String, Trie> children;

    public Trie() {
        ref = -1;
        children = new HashMap<String, Trie>();
    }
}