|
56 | 56 |
|
57 | 57 | <!-- 这里可写通用的实现逻辑 -->
|
58 | 58 |
|
59 |
| -**方法一:排序 + 前缀树** |
| 59 | +**方法一:排序** |
| 60 | + |
| 61 | +我们先将数组 `folder` 按照字典序排序,然后遍历数组,对于当前遍历到的文件夹 $f$,如果它的长度大于等于答案数组中最后一个文件夹的长度,并且它的前缀包含答案数组的最后一个文件夹再加上一个 `/`,则说明 $f$ 是答案数组中最后一个文件夹的子文件夹,我们不需要将其加入答案数组中。否则,我们将 $f$ 加入答案数组中。 |
| 62 | + |
| 63 | +遍历结束后,答案数组中的文件夹即为题目要求的答案。 |
| 64 | + |
| 65 | +时间复杂度 $O(n \times \log n \times m)$,空间复杂度 $O(m)$。其中 $n$ 和 $m$ 分别为数组 `folder` 的长度和数组 `folder` 中字符串的最大长度。 |
| 66 | + |
| 67 | +**方法二:字典树** |
| 68 | + |
| 69 | +我们可以使用字典树存储数组 `folder` 中的所有文件夹。字典树的每个节点包含 `children` 字段,用于存储当前节点的子节点,以及 `fid` 字段,用于存储当前节点对应的文件夹在数组 `folder` 中的下标。 |
| 70 | + |
| 71 | +对于数组 `folder` 中的每个文件夹 $f$,我们先将 $f$ 按照 `/` 分割成若干个子串,然后从根节点开始,依次将子串加入字典树中。接下来,我们从根节点开始搜索字典树,如果当前节点的 `fid` 字段不为 `-1`,则说明当前节点对应的文件夹是答案数组中的一个文件夹,我们将其加入答案数组并且返回。否则,我们递归地搜索当前节点的所有子节点,最终返回答案数组。 |
| 72 | + |
| 73 | +时间复杂度 $O(n \times m)$,空间复杂度 $O(n \times m)$。其中 $n$ 和 $m$ 分别为数组 `folder` 的长度和数组 `folder` 中字符串的最大长度。 |
60 | 74 |
|
61 | 75 | <!-- tabs:start -->
|
62 | 76 |
|
63 | 77 | ### **Python3**
|
64 | 78 |
|
65 | 79 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
66 | 80 |
|
| 81 | +```python |
| 82 | +class Solution: |
| 83 | + def removeSubfolders(self, folder: List[str]) -> List[str]: |
| 84 | + folder.sort() |
| 85 | + ans = [folder[0]] |
| 86 | + for f in folder[1:]: |
| 87 | + m, n = len(ans[-1]), len(f) |
| 88 | + if m >= n or not (ans[-1] == f[:m] and f[m] == '/'): |
| 89 | + ans.append(f) |
| 90 | + return ans |
| 91 | +``` |
| 92 | + |
67 | 93 | ```python
|
68 | 94 | class Trie:
|
69 | 95 | def __init__(self):
|
70 | 96 | self.children = {}
|
71 |
| - self.is_end = False |
| 97 | + self.fid = -1 |
72 | 98 |
|
73 |
| - def insert(self, w): |
| 99 | + def insert(self, i, f): |
74 | 100 | node = self
|
75 |
| - ps = w.split('/') |
| 101 | + ps = f.split('/') |
76 | 102 | for p in ps[1:]:
|
77 | 103 | if p not in node.children:
|
78 | 104 | node.children[p] = Trie()
|
79 | 105 | node = node.children[p]
|
80 |
| - node.is_end = True |
| 106 | + node.fid = i |
81 | 107 |
|
82 |
| - def search(self, w): |
83 |
| - node = self |
84 |
| - ps = w.split('/') |
85 |
| - for p in ps[1:]: |
86 |
| - if p not in node.children: |
87 |
| - return False |
88 |
| - node = node.children[p] |
89 |
| - if node.is_end: |
90 |
| - return True |
91 |
| - return False |
| 108 | + def search(self): |
| 109 | + def dfs(root): |
| 110 | + if root.fid != -1: |
| 111 | + ans.append(root.fid) |
| 112 | + return |
| 113 | + for child in root.children.values(): |
| 114 | + dfs(child) |
92 | 115 |
|
| 116 | + ans = [] |
| 117 | + dfs(self) |
| 118 | + return ans |
93 | 119 |
|
94 | 120 | class Solution:
|
95 | 121 | def removeSubfolders(self, folder: List[str]) -> List[str]:
|
96 | 122 | trie = Trie()
|
97 |
| - folder.sort(key=lambda x: len(x.split('/'))) |
98 |
| - ans = [] |
99 |
| - for v in folder: |
100 |
| - if not trie.search(v): |
101 |
| - trie.insert(v) |
102 |
| - ans.append(v) |
103 |
| - return ans |
| 123 | + for i, f in enumerate(folder): |
| 124 | + trie.insert(i, f) |
| 125 | + return [folder[i] for i in trie.search()] |
104 | 126 | ```
|
105 | 127 |
|
106 | 128 | ### **Java**
|
107 | 129 |
|
108 | 130 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
109 | 131 |
|
| 132 | +```java |
| 133 | +class Solution { |
| 134 | + public List<String> removeSubfolders(String[] folder) { |
| 135 | + Arrays.sort(folder); |
| 136 | + List<String> ans = new ArrayList<>(); |
| 137 | + ans.add(folder[0]); |
| 138 | + for (int i = 1; i < folder.length; ++i) { |
| 139 | + int m = ans.get(ans.size() - 1).length(); |
| 140 | + int n = folder[i].length(); |
| 141 | + if (m >= n || !(ans.get(ans.size() - 1).equals(folder[i].substring(0, m)) && folder[i].charAt(m) == '/')) { |
| 142 | + ans.add(folder[i]); |
| 143 | + } |
| 144 | + } |
| 145 | + return ans; |
| 146 | + } |
| 147 | +} |
| 148 | +``` |
| 149 | + |
110 | 150 | ```java
|
111 | 151 | class Trie {
|
112 |
| - Map<String, Trie> children = new HashMap<>(); |
113 |
| - boolean isEnd; |
| 152 | + private Map<String, Trie> children = new HashMap<>(); |
| 153 | + private int fid = -1; |
114 | 154 |
|
115 |
| - void insert(String w) { |
| 155 | + public void insert(int fid, String f) { |
116 | 156 | Trie node = this;
|
117 |
| - String[] ps = w.split("/"); |
| 157 | + String[] ps = f.split("/"); |
118 | 158 | for (int i = 1; i < ps.length; ++i) {
|
119 | 159 | String p = ps[i];
|
120 | 160 | if (!node.children.containsKey(p)) {
|
121 | 161 | node.children.put(p, new Trie());
|
122 | 162 | }
|
123 | 163 | node = node.children.get(p);
|
124 | 164 | }
|
125 |
| - node.isEnd = true; |
| 165 | + node.fid = fid; |
126 | 166 | }
|
127 | 167 |
|
128 |
| - boolean search(String w) { |
129 |
| - Trie node = this; |
130 |
| - String[] ps = w.split("/"); |
131 |
| - for (int i = 1; i < ps.length; ++i) { |
132 |
| - String p = ps[i]; |
133 |
| - if (!node.children.containsKey(p)) { |
134 |
| - return false; |
135 |
| - } |
136 |
| - node = node.children.get(p); |
137 |
| - if (node.isEnd) { |
138 |
| - return true; |
139 |
| - } |
| 168 | + public List<Integer> search() { |
| 169 | + List<Integer> ans = new ArrayList<>(); |
| 170 | + dfs(this, ans); |
| 171 | + return ans; |
| 172 | + } |
| 173 | + |
| 174 | + private void dfs(Trie root, List<Integer> ans) { |
| 175 | + if (root.fid != -1) { |
| 176 | + ans.add(root.fid); |
| 177 | + return; |
| 178 | + } |
| 179 | + for (var child : root.children.values()) { |
| 180 | + dfs(child, ans); |
140 | 181 | }
|
141 |
| - return false; |
142 | 182 | }
|
143 | 183 | }
|
144 | 184 |
|
145 | 185 | class Solution {
|
146 | 186 | public List<String> removeSubfolders(String[] folder) {
|
147 |
| - Arrays.sort(folder, (a, b) -> a.split("/").length - b.split("/").length); |
148 | 187 | Trie trie = new Trie();
|
| 188 | + for (int i = 0; i < folder.length; ++i) { |
| 189 | + trie.insert(i, folder[i]); |
| 190 | + } |
149 | 191 | List<String> ans = new ArrayList<>();
|
150 |
| - for (String v : folder) { |
151 |
| - if (!trie.search(v)) { |
152 |
| - trie.insert(v); |
153 |
| - ans.add(v); |
154 |
| - } |
| 192 | + for (int i : trie.search()) { |
| 193 | + ans.add(folder[i]); |
155 | 194 | }
|
156 | 195 | return ans;
|
157 | 196 | }
|
158 | 197 | }
|
159 | 198 | ```
|
160 | 199 |
|
| 200 | +### **C++** |
| 201 | + |
| 202 | +```cpp |
| 203 | +class Solution { |
| 204 | +public: |
| 205 | + vector<string> removeSubfolders(vector<string>& folder) { |
| 206 | + sort(folder.begin(), folder.end()); |
| 207 | + vector<string> ans = {folder[0]}; |
| 208 | + for (int i = 1; i < folder.size(); ++i) { |
| 209 | + int m = ans.back().size(); |
| 210 | + int n = folder[i].size(); |
| 211 | + if (m >= n || !(ans.back() == folder[i].substr(0, m) && folder[i][m] == '/')) { |
| 212 | + ans.emplace_back(folder[i]); |
| 213 | + } |
| 214 | + } |
| 215 | + return ans; |
| 216 | + } |
| 217 | +}; |
| 218 | +``` |
| 219 | +
|
| 220 | +```cpp |
| 221 | +class Trie { |
| 222 | +public: |
| 223 | + void insert(int fid, string& f) { |
| 224 | + Trie* node = this; |
| 225 | + vector<string> ps = split(f, '/'); |
| 226 | + for (int i = 1; i < ps.size(); ++i) { |
| 227 | + auto& p = ps[i]; |
| 228 | + if (!node->children.count(p)) { |
| 229 | + node->children[p] = new Trie(); |
| 230 | + } |
| 231 | + node = node->children[p]; |
| 232 | + } |
| 233 | + node->fid = fid; |
| 234 | + } |
| 235 | +
|
| 236 | + vector<int> search() { |
| 237 | + vector<int> ans; |
| 238 | + function<void(Trie*)> dfs = [&](Trie* root) { |
| 239 | + if (root->fid != -1) { |
| 240 | + ans.push_back(root->fid); |
| 241 | + return; |
| 242 | + } |
| 243 | + for (auto& [_, child] : root->children) { |
| 244 | + dfs(child); |
| 245 | + } |
| 246 | + }; |
| 247 | + dfs(this); |
| 248 | + return ans; |
| 249 | + } |
| 250 | +
|
| 251 | + vector<string> split(string& s, char delim) { |
| 252 | + stringstream ss(s); |
| 253 | + string item; |
| 254 | + vector<string> res; |
| 255 | + while (getline(ss, item, delim)) { |
| 256 | + res.emplace_back(item); |
| 257 | + } |
| 258 | + return res; |
| 259 | + } |
| 260 | +
|
| 261 | +private: |
| 262 | + unordered_map<string, Trie*> children; |
| 263 | + int fid = -1; |
| 264 | +}; |
| 265 | +
|
| 266 | +class Solution { |
| 267 | +public: |
| 268 | + vector<string> removeSubfolders(vector<string>& folder) { |
| 269 | + Trie* trie = new Trie(); |
| 270 | + for (int i = 0; i < folder.size(); ++i) { |
| 271 | + trie->insert(i, folder[i]); |
| 272 | + } |
| 273 | + vector<string> ans; |
| 274 | + for (int i : trie->search()) { |
| 275 | + ans.emplace_back(folder[i]); |
| 276 | + } |
| 277 | + return ans; |
| 278 | + } |
| 279 | +}; |
| 280 | +``` |
| 281 | + |
161 | 282 | ### **Go**
|
162 | 283 |
|
| 284 | +```go |
| 285 | +func removeSubfolders(folder []string) []string { |
| 286 | + sort.Strings(folder) |
| 287 | + ans := []string{folder[0]} |
| 288 | + for _, f := range folder[1:] { |
| 289 | + m, n := len(ans[len(ans)-1]), len(f) |
| 290 | + if m >= n || !(ans[len(ans)-1] == f[:m] && f[m] == '/') { |
| 291 | + ans = append(ans, f) |
| 292 | + } |
| 293 | + } |
| 294 | + return ans |
| 295 | +} |
| 296 | +``` |
| 297 | + |
163 | 298 | ```go
|
164 | 299 | type Trie struct {
|
165 | 300 | children map[string]*Trie
|
|
0 commit comments