一、Problem
你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹。
我们这样定义「子文件夹」:
如果文件夹 folder[i] 位于另一个文件夹 folder[j] 下,那么 folder[i] 就是 folder[j] 的子文件夹。
文件夹的「路径」是由一个或多个按以下格式串联形成的字符串:
/ 后跟一个或者多个小写英文字母。
例如,/leetcode 和 /leetcode/problems 都是有效的路径,而空字符串和 / 不是。
输入:folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]
输出:["/a","/c/d","/c/f"]
解释:"/a/b/" 是 "/a" 的子文件夹,而 "/c/d/e" 是 "/c/d" 的子文件夹。
输入:folder = ["/a/b/c","/a/b/d","/a/b/ca"]
输出:["/a/b/c","/a/b/ca","/a/b/d"]
提示:
1 <= folder.length <= 4 * 10^4
2 <= folder[i].length <= 100
folder[i] 只包含小写字母和 /
folder[i] 总是以字符 / 起始
每个文件夹名都是唯一的
方法一:记录根目录
双重循环检查很简单,但会超时。
- 可以先对 folders 数组排序(字典序),那么子父文件夹就越有可能被放到相邻位置。
- 如果当前文件夹 是上一个父文件夹的子文件夹,那么将 root 设置为当前文件夹,并跳过当前文件夹。
- 然后继续枚举下去,重复第 2 点。
细节:这里的判断子父文件夹的时候不能直接比较两个字符串,因为给出的某些文件夹是没有 / 结尾的,我们要先将 / 添加到根目录字符串的后面,否则会 WA,比如
folder = ["/a/b/c","/a/b/d","/a/b/ca"]
这就只会输出 ["/a/b/c","/a/b/d"]
因为这里误认为 "/a/b/ca" 是 "/a/b/c" 的子文件夹了
class Solution {
boolean startsWith(String cur, String root) {
int i;
for (i = 0; i < root.length(); i++) if (root.charAt(i) != cur.charAt(i)) {
return false;
}
return cur.charAt(--i) == '/';
}
public List<String> removeSubfolders(String[] fs) {
Arrays.sort(fs);
List<String> ans = new LinkedList<>();
ans.add(fs[0]);
String root = fs[0] + "/";
int n = fs.length;
for (int i = 1; i < n; i++) {
if (!startsWith(fs[i], root)) {
root = fs[i] + "/";
ans.add(fs[i]);
}
}
return ans;
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
方法二:前缀树
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,