LC 388. 文件的最长绝对路径
题目描述
这是 LeetCode 上的 388. 文件的最长绝对路径 ,难度为 中等。
假设有一个同时存储文件和目录的文件系统。下图展示了文件系统的一个示例:
这里将 dir
作为根目录中的唯一目录。dir
包含两个子目录 subdir1
和 subdir2
。
subdir1
包含文件 file1.ext
和子目录 subsubdir1
;subdir2
包含子目录 subsubdir2
,该子目录下包含文件 file2.ext
。
在文本格式中,如下所示(⟶表示制表符):1
2
3
4
5
6
7dir
⟶ subdir1
⟶ ⟶ file1.ext
⟶ ⟶ subsubdir1
⟶ subdir2
⟶ ⟶ subsubdir2
⟶ ⟶ ⟶ file2.ext
如果是代码表示,上面的文件系统可以写为 "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
。'\n'
和 '\t'
分别是换行符和制表符。
文件系统中的每个文件和文件夹都有一个唯一的 绝对路径 ,即必须打开才能到达文件/目录所在位置的目录顺序,所有路径用 ‘/‘ 连接。上面例子中,指向 file2.ext
的 绝对路径 是 "dir/subdir2/subsubdir2/file2.ext"
。每个目录名由字母、数字和/或空格组成,每个文件名遵循 name.extension
的格式,其中 name
和 extension
由字母、数字和/或空格组成。
给定一个以上述格式表示文件系统的字符串 input
,返回文件系统中 指向 文件 的 最长绝对路径 的长度 。 如果系统中没有文件,返回 $0$。
示例 1:1
2
3
4
5输入:input = "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext"
输出:20
解释:只有一个文件,绝对路径为 "dir/subdir2/file.ext" ,路径长度 20
示例 2:1
2
3
4
5
6
7
8输入:input = "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
输出:32
解释:存在两个文件:
"dir/subdir1/file1.ext" ,路径长度 21
"dir/subdir2/subsubdir2/file2.ext" ,路径长度 32
返回 32 ,因为这是最长的路径
示例 3:1
2
3
4
5输入:input = "a"
输出:0
解释:不存在任何文件
示例 4:1
2
3
4
5
6输入:input = "file1.txt\nfile2.txt\nlongfile.txt"
输出:12
解释:根目录下有 3 个文件。
因为根目录中任何东西的绝对路径只是名称本身,所以答案是 "longfile.txt" ,路径长度为 12
提示:
- $1 <= input.length <= 10^4$
input
可能包含小写或大写的英文字母,一个换行符'\n'
,一个制表符'\t'
,一个点'.'
,一个空格' '
,和数字。
模拟 + 哈希表
为了方便,我们将 input
替换为 s
。
对于每一个文件或文件夹而言,我们可以通过访问到结尾(\n
)的方式取得,记为 cur
,然后根据 cur
前面有多少个 \t
得知其所在的层级,假设当前其所在层级为 level
,那么它自然归属到最新一个层级为 level - 1
的文件夹中,因此我们可以使用哈希表记录每个层级最新的文件夹路径,通过字符串拼接的方式得到 cur
所在的完整路径 path
,并在处理整个 s
过程中,统计长度最大的文件路径。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Solution {
public int lengthLongestPath(String s) {
Map<Integer, String> map = new HashMap<>();
int n = s.length();
String ans = null;
for (int i = 0; i < n; ) {
int level = 0;
while (i < n && s.charAt(i) == '\t' && ++level >= 0) i++;
int j = i;
boolean isDir = true;
while (j < n && s.charAt(j) != '\n') {
if (s.charAt(j++) == '.') isDir = false;
}
String cur = s.substring(i, j);
String prev = map.getOrDefault(level - 1, null);
String path = prev == null ? cur : prev + "/" + cur;
if (isDir) map.put(level, path);
else if (ans == null || path.length() > ans.length()) ans = path;
i = j + 1;
}
return ans == null ? 0 : ans.length();
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
优化
上述做法只是为了方便我们输出具体方案。
实际上,我们只关心最终的路径长度,而不关心具体路径,因此容易将解法一修改为只记录长度,而不记录路径的做法,从而避免掉字符串拼接带来的消耗,同时利用 s
的长度数据范围,使用数组来替代常数较大的哈希表。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Solution {
static int[] hash = new int[10010];
public int lengthLongestPath(String s) {
Arrays.fill(hash, -1);
int n = s.length(), ans = 0;
for (int i = 0; i < n; ) {
int level = 0;
while (i < n && s.charAt(i) == '\t' && ++level >= 0) i++;
int j = i;
boolean isDir = true;
while (j < n && s.charAt(j) != '\n') {
if (s.charAt(j++) == '.') isDir = false;
}
Integer cur = j - i;
Integer prev = level - 1 >= 0 ? hash[level - 1] : -1;
Integer path = prev + 1 + cur;
if (isDir) hash[level] = path;
else if (path > ans) ans = path;
i = j + 1;
}
return ans;
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(C)$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.388
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!