你是一位系统管理员,手里有一份文件夹列表 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]
总是以字符'/'
起始- 每个文件夹名都是 唯一 的
方法一:排序 + 前缀树
class Trie:
def __init__(self):
self.children = {}
self.is_end = False
def insert(self, w):
node = self
ps = w.split('/')
for p in ps[1:]:
if p not in node.children:
node.children[p] = Trie()
node = node.children[p]
node.is_end = True
def search(self, w):
node = self
ps = w.split('/')
for p in ps[1:]:
if p not in node.children:
return False
node = node.children[p]
if node.is_end:
return True
return False
class Solution:
def removeSubfolders(self, folder: List[str]) -> List[str]:
trie = Trie()
folder.sort(key=lambda x: len(x.split('/')))
ans = []
for v in folder:
if not trie.search(v):
trie.insert(v)
ans.append(v)
return ans
class Trie {
Map<String, Trie> children = new HashMap<>();
boolean isEnd;
void insert(String w) {
Trie node = this;
String[] ps = w.split("/");
for (int i = 1; i < ps.length; ++i) {
String p = ps[i];
if (!node.children.containsKey(p)) {
node.children.put(p, new Trie());
}
node = node.children.get(p);
}
node.isEnd = true;
}
boolean search(String w) {
Trie node = this;
String[] ps = w.split("/");
for (int i = 1; i < ps.length; ++i) {
String p = ps[i];
if (!node.children.containsKey(p)) {
return false;
}
node = node.children.get(p);
if (node.isEnd) {
return true;
}
}
return false;
}
}
class Solution {
public List<String> removeSubfolders(String[] folder) {
Arrays.sort(folder, (a, b) -> a.split("/").length - b.split("/").length);
Trie trie = new Trie();
List<String> ans = new ArrayList<>();
for (String v : folder) {
if (!trie.search(v)) {
trie.insert(v);
ans.add(v);
}
}
return ans;
}
}
type Trie struct {
children map[string]*Trie
isEnd bool
}
func newTrie() *Trie {
m := map[string]*Trie{}
return &Trie{children: m}
}
func (this *Trie) insert(w string) {
node := this
for _, p := range strings.Split(w, "/")[1:] {
if _, ok := node.children[p]; !ok {
node.children[p] = newTrie()
}
node, _ = node.children[p]
}
node.isEnd = true
}
func (this *Trie) search(w string) bool {
node := this
for _, p := range strings.Split(w, "/")[1:] {
if _, ok := node.children[p]; !ok {
return false
}
node, _ = node.children[p]
if node.isEnd {
return true
}
}
return false
}
func removeSubfolders(folder []string) []string {
sort.Slice(folder, func(i, j int) bool {
return len(strings.Split(folder[i], "/")) < len(strings.Split(folder[j], "/"))
})
trie := newTrie()
var ans []string
for _, v := range folder {
if !trie.search(v) {
trie.insert(v)
ans = append(ans, v)
}
}
return ans
}